Speeding Up Angular Lists with trackBy: A Practical Guide
Learn why Angular’s trackBy function matters, how to implement it correctly, and how it plays with OnPush change detection. A concrete example shows measurable DOM savings and the trade‑off between simplicity and performance.
26 Aug 2025, 11:18 UTC

Why trackBy is a Game Changer for Large Lists
When an Angular component renders a list with *ngFor, the framework must decide which DOM nodes to keep, update, or destroy every time the underlying array changes. By default it compares array items by reference. If the array is recreated or items are re‑ordered, Angular will destroy all nodes and rebuild them, causing heavy DOM churn even when only a few items changed.
The trackBy function gives Angular a stable key for each item. With a unique identifier, Angular can reuse existing nodes for unchanged items, dramatically reducing the number of DOM operations and the time spent in change‑detection cycles.
Implementing trackBy Correctly
To get the benefit, the trackBy callback must return a value that uniquely identifies an item and never changes while the item remains in the list. A common pattern is to use a numeric ID or a UUID. Returning the array index or a mutable property (e.g., a timestamp) defeats the purpose and can cause visual glitches.
// items.component.ts
import { Component, ChangeDetectionStrategy } from '@angular/core';
interface Item {
id: number;
name: string;
}
@Component({
selector: 'app-items',
template: `
-
{{item.name}}
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ItemsComponent {
items: Item[] = [];
// Stable identifier
trackById(index: number, item: Item): number {
return item.id;
}
}
In this example, trackById returns the id field. Because id is immutable for each item, Angular can safely reuse the <li> elements when items changes.
Measuring the Impact
To confirm that trackBy reduces DOM creation, run the following steps in a browser dev environment:
- Populate
itemswith 1,000 objects. - Use the Chrome DevTools Performance tab to record a rapid update cycle: replace the array with a new one that has the same items in a different order.
- Count the number of
linodes created during the cycle. - Repeat the test with
trackByremoved (or returning the index). - Compare the counts: with a stable
idyou should see far fewer new nodes, often only a handful corresponding to truly changed items.
When the component also uses ChangeDetectionStrategy.OnPush, Angular skips change detection for the component unless an @Input changes, an event occurs, or an Observable emits. trackBy still works because the framework uses the key to decide whether a DOM node needs to be updated. This combination yields the fastest possible rendering for large, frequently updated lists.
Trade‑Offs and Limitations
- Complexity vs. Simplicity: Using an index as the key is trivial but ineffective for reordered lists. Implementing a stable ID requires more upfront planning, especially when items are generated on the fly or come from a server.
- Mutable Keys: If the value returned by
trackBychanges (e.g., a timestamp or a computed property that updates), Angular will treat the item as new and recreate its DOM node, negating performance gains. - Structural Directive Conflicts: Applying
*ngIfor other structural directives on the same element as*ngForcan interfere withtrackBy. KeeptrackByon the*ngForcontainer only. - Memory Footprint: Storing a large array of objects with unique IDs consumes more memory than a simple list of strings. Evaluate memory usage if the list is extremely large.
Practical Checklist Before You Deploy
- Ensure each item has a truly unique and immutable key (e.g., database primary key).
- Verify that the key never changes during the component’s lifecycle.
- Confirm that
*ngForis the only structural directive on the container element. - Profile the component with and without
trackByin a realistic data‑update scenario. - When using
OnPush, trigger updates via @Input changes, events, or Observables; avoid manual change detection if possible.
Takeaway
Angular’s trackBy turns a potentially expensive list rendering into a lean operation by reusing DOM nodes. Pair it with the OnPush strategy for maximum performance, but only if you can guarantee a stable identifier. Measure the DOM churn in real scenarios to validate the benefit and avoid pitfalls like mutable keys or directive conflicts.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.