How Does track Improve @for Performance in Angular?
What is track?
In modern Angular, track is used with the @for block to tell Angular how to uniquely identify each item in a collection.
Example:
@for (product of products; track product.id) {
<p>{{ product.name }}</p>
}
Here:
track product.id
tells Angular:
Use
product.idas the unique identity of each product.
This allows Angular to determine efficiently which list items were added, removed, moved, or retained, and reuse existing DOM elements where possible.
Full Simple Sample Program
product.ts
export interface Product {
id: number;
name: string;
price: number;
}
product-list.ts
import { Component } from '@angular/core';
import { Product } from './product';
@Component({
selector: 'app-product-list',
standalone: true,
templateUrl: './product-list.html'
})
export class ProductList {
products: Product[] = [
{
id: 1,
name: 'Laptop',
price: 50000
},
{
id: 2,
name: 'Mouse',
price: 1000
},
{
id: 3,
name: 'Keyboard',
price: 2000
}
];
addProduct(): void {
this.products = [
...this.products,
{
id: 4,
name: 'Monitor',
price: 15000
}
];
}
removeProduct(): void {
this.products =
this.products.filter(product => product.id !== 2);
}
}
product-list.html
<h2>Products</h2>
<button (click)="addProduct()">
Add Product
</button>
<button (click)="removeProduct()">
Remove Mouse
</button>
<hr>
@for (product of products; track product.id) {
<div>
{{ product.id }} -
{{ product.name }} -
₹{{ product.price }}
</div>
}
How Does It Work?
Initially:
ID Product
----------------
1 Laptop
2 Mouse
3 Keyboard
Angular renders:
DOM Element 1 → Product ID 1
DOM Element 2 → Product ID 2
DOM Element 3 → Product ID 3
Because we wrote:
track product.id
Angular associates each rendered item with its ID.
Adding a Product
When:
this.products = [
...this.products,
{
id: 4,
name: 'Monitor',
price: 15000
}
];
the collection becomes:
Before:
1 → Laptop
2 → Mouse
3 → Keyboard
After:
1 → Laptop
2 → Mouse
3 → Keyboard
4 → Monitor
Angular can use the tracked IDs to determine:
ID 1 → already exists
ID 2 → already exists
ID 3 → already exists
ID 4 → NEW
Therefore Angular can reuse the existing DOM for IDs 1, 2, and 3, and create the required DOM for ID 4.
Conceptually:
Existing DOM
────────────
[1 Laptop] ← reuse
[2 Mouse] ← reuse
[3 Keyboard] ← reuse
+ create
[4 Monitor]
This reduces unnecessary DOM work.
Removing an Item
Suppose we execute:
this.products =
this.products.filter(product => product.id !== 2);
Before:
1 → Laptop
2 → Mouse
3 → Keyboard
After:
1 → Laptop
3 → Keyboard
Because Angular tracks:
track product.id
it can determine:
ID 1 → still exists
ID 2 → removed
ID 3 → still exists
So conceptually:
[1 Laptop] → reuse
[2 Mouse] → remove
[3 Keyboard] → reuse
Angular does not need to treat every remaining product as a completely unrelated item.
Why Is This Important?
DOM operations can be relatively expensive.
For example, creating/removing elements can involve:
Create DOM node
↓
Insert/remove node
↓
Update properties
↓
Browser layout/style work
↓
Painting
For three products, the difference may be negligible.
But imagine:
10,000 products
If only one item changes, you want Angular to identify that change efficiently rather than unnecessarily recreating DOM associated with unchanged items.
Example with Sorting
track is also valuable when items move.
Initial collection:
1 → Laptop
2 → Mouse
3 → Keyboard
After sorting:
3 → Keyboard
1 → Laptop
2 → Mouse
With:
track product.id
Angular knows:
Product 3 is still Product 3
Product 1 is still Product 1
Product 2 is still Product 2
Only their positions changed.
Conceptually:
Before
[1 Laptop]
[2 Mouse]
[3 Keyboard]
After
[3 Keyboard] ↑
[1 Laptop] ↓
[2 Mouse] ↓
Angular can reconcile/move the existing rendered items instead of treating them as entirely new products.
track product.id vs track $index
Angular also allows:
@for (product of products; track $index) {
But this tracks items according to their position.
For example:
Index Product
-----------------
0 Laptop
1 Mouse
2 Keyboard
Suppose Mouse is removed:
Before
0 → Laptop
1 → Mouse
2 → Keyboard
After
0 → Laptop
1 → Keyboard
Now Keyboard changed from:
index 2
to:
index 1
So position isn't a stable representation of the product's identity.
For dynamic lists, prefer a stable unique identifier:
track product.id
When Is track $index Okay?
$index can be reasonable for a list that is effectively static and won't be:
- reordered
- inserted into
- removed from
For example:
days = [
'Monday',
'Tuesday',
'Wednesday'
];
You could use:
@for (day of days; track $index) {
<p>{{ day }}</p>
}
But for database/API entities such as:
Products
Customers
Orders
Articles
Employees
prefer the entity's stable ID:
track product.id
track customer.id
track order.id
track article.id
track with API Data
Suppose your ASP.NET Core API returns:
[
{
"id": 101,
"name": "Laptop"
},
{
"id": 102,
"name": "Mouse"
},
{
"id": 103,
"name": "Keyboard"
}
]
Angular:
products = signal<Product[]>([]);
Template:
@for (product of products(); track product.id) {
<div>
{{ product.name }}
</div>
}
This is a good real-world pattern because the database/API ID provides a stable identity.
SQL Server
↓
ASP.NET Core Web API
↓
JSON
id = 101
id = 102
id = 103
↓
Angular
↓
track product.id
What Happens If Objects Are Recreated?
This is another important benefit.
Suppose an API initially returns objects:
[
{ id: 1, name: 'Laptop' },
{ id: 2, name: 'Mouse' }
]
Later you receive newly created JavaScript objects:
[
{ id: 1, name: 'Laptop' },
{ id: 2, name: 'Mouse' }
]
Although these may be different JavaScript object references, their IDs remain:
1
2
With:
track product.id
Angular can use those stable keys to associate the new collection entries with the existing rendered items.
That's one reason IDs are excellent tracking keys.
What Should Be Used as the Tracking Key?
The tracking expression should preferably produce a value that is:
- Unique among items
- Stable for that item's lifetime
Good
track product.id
track customer.customerId
track article.id
Potentially problematic
track product.name
if names can change or duplicate.
Usually not ideal for dynamic collections
track $index
because positions change when items are inserted, removed, or reordered.
Before Modern @for
Before Angular's modern control-flow syntax, you commonly used *ngFor with trackBy.
For example:
<div
*ngFor="let product of products; trackBy: trackByProductId">
{{ product.name }}
</div>
Component:
trackByProductId(
index: number,
product: Product
): number {
return product.id;
}
Modern Angular simplifies this:
@for (product of products; track product.id) {
{{ product.name }}
}
So conceptually:
Older Angular
*ngFor
+
trackBy function
Modern Angular
@for
+
track expression
track Does Not Mean "Run Change Detection Only for That Item"
This distinction is important.
track is primarily about collection reconciliation and DOM identity.
It helps Angular determine:
Which item stayed?
Which item was added?
Which item was removed?
Which item moved?
It is not the same concept as:
ChangeDetectionStrategy.OnPush
Think:
OnPush
↓
Optimize component/view checking
track
↓
Optimize list reconciliation and DOM reuse
Both improve performance, but in different ways.
Key Points
trackuniquely identifies items rendered by@for.- Prefer a stable unique key such as:
track product.id
- Angular uses the key to efficiently reconcile old and new collections.
- Existing DOM can be reused when the same tracked item remains.
- Angular can efficiently identify additions, removals, and moves.
- This becomes especially valuable for large or frequently changing lists.
- Avoid $index for lists that are frequently inserted, removed, or reordered.
- Database/API IDs are usually excellent tracking keys.
- Modern
@for ... trackreplaces many older*ngFor + trackByuse cases. trackoptimizes list reconciliation;OnPushoptimizes view/component checking.
Interview Questions and Answers
1. What is track in Angular @for?
track tells Angular how to uniquely identify each item in a collection.
@for (product of products; track product.id) {
{{ product.name }}
}
2. How does track improve performance?
It allows Angular to associate collection items with existing rendered DOM, so Angular can efficiently determine which items were added, removed, retained, or moved.
3. Why use product.id?
Because an ID is normally unique and stable.
track product.id
This allows Angular to preserve an item's identity even when the array is reordered or recreated.
4. Can we use $index?
Yes:
@for (product of products; track $index) {
But $index is better suited to static collections. For dynamic lists that are reordered or modified, prefer a stable unique ID.
5. What happens when an item is removed?
With:
track product.id
Angular can identify the removed ID and remove its corresponding rendered view while retaining the views for unchanged IDs.
6. Does track prevent change detection?
No.
track optimizes how Angular reconciles collections and manages rendered list items. It is not a replacement for change detection.
7. What was used before @for?
With *ngFor, Angular commonly used a trackBy function:
*ngFor="let product of products; trackBy: trackByProductId"
Modern Angular provides the simpler:
@for (product of products; track product.id)
Interview Quick Answer
trackimproves@forperformance by giving Angular a stable identity for each collection item. Angular can then efficiently determine which items were added, removed, retained, or moved and reuse existing DOM/views instead of unnecessarily recreating them. For dynamic API/database data, a stable unique ID such astrack product.idis usually the preferred choice.