trackBy / track
Without tracking, Angular destroys and recreates every DOM element in a list whenever the data changes. track (and the legacy trackBy) tells Angular how to identify each item so it only touches what actually changed.
Modern Control Flow (@for — Angular 17+)
track is required in @for — Angular enforces the best practice at compile time:
@for (album of albums; track album.id) {
<app-album-card [album]="album" />
}
track album.id— use a stable, unique propertytrack $index— valid but less efficient (DOM is recycled by position, not identity)
Legacy *ngFor with trackBy
For Angular 16 and below, or when still using *ngFor:
@Component({
template: `
<div *ngFor="let album of albums; trackBy: trackById">
{{ album.title }}
</div>
`
})
export class AlbumListComponent {
trackById(index: number, album: Album): number {
return album.id;
}
}
The trackBy function receives (index, item) and must return a stable unique value — usually the item's ID.
Why It Matters
| Scenario | Without track | With track |
|---|---|---|
| API re-fetches same list | All DOM nodes destroyed and recreated | Nothing changes |
| One item added to end | All nodes recreated | One node inserted |
| One item changes in middle | All nodes recreated | Only that node updated |
| Items reordered | All nodes recreated | Nodes moved in place |
The performance gain is significant for long lists or lists that update frequently.
What to Track
<!-- ✅ Stable unique ID -->
@for (item of items; track item.id) { ... }
<!-- ✅ Compound key if no single unique field -->
@for (item of items; track item.type + '-' + item.id) { ... }
<!-- ⚠️ Index only — safe for static lists, poor for dynamic -->
@for (item of items; track $index) { ... }
<!-- ❌ Tracking an unstable value defeats the purpose -->
@for (item of items; track item) { ... }
Key Point
In modern Angular, @for with track replaces *ngFor + trackBy. The explicit track expression is shorter, inlined, and required — you cannot forget it.