Choosing the Right ng‑repeat Strategy for Large AngularJS Lists
When rendering thousands of items in AngularJS, the choice of ng‑repeat strategy determines performance. This guide compares default, track‑by $index, and track‑by unique id approaches, explains trade‑offs, and walks through a benchmark that shows why a stable id is the best choice.
13 Mar 2026, 10:34 UTC

Decision: How to Render Thousands of Items Efficiently
When an AngularJS application needs to display a list of 1,000+ items that may change, add, or reorder, the default ng-repeat can become a bottleneck. The decision boils down to which track‑by strategy best balances CPU usage, DOM stability, and correctness.
Constraints to Consider
- AngularJS version 1.3+ (track‑by is unavailable before 1.3).
- Each list item must have a stable, unique key that does not mutate during the list’s lifetime.
- The list may be subject to frequent
push,splice, or reorder operations. - The application runs in modern browsers where Chrome DevTools can be used for validation.
Comparison of ng‑repeat Options
| Option | Description | CPU Impact | DOM Churn | Correctness for Reorder |
|---|---|---|---|---|
Default (no track‑by) | AngularJS recreates every DOM element on each digest. | High – each digest triggers full re‑render. | High – every update forces new nodes. | Accurate but expensive. |
track‑by $index | Reuses nodes based on array index. | Moderate – reduces DOM creation but still sensitive to index shifts. | Low – index changes cause node recreation. | Fails to preserve data when items reorder. |
track‑by item.id | Reuses nodes based on a stable unique identifier. | Low – only nodes that actually change are updated. | Low – minimal DOM mutations. | Correct for reordering and key changes. |
Trade‑offs Explained
- Default is simple but unacceptable for large, dynamic lists because each digest rebuilds the entire list, consuming CPU and memory.
- $index mitigates churn but breaks when items move; the UI will show stale or duplicate data if the array is shuffled.
- Unique ID offers the best performance and correctness, provided the ID remains immutable. If the key changes after binding, AngularJS will still recreate the node, negating performance gains.
- For legacy AngularJS (<1.3),
track‑byis ignored; developers must refactor to a newer version or accept the default behavior.
Concrete Implementation Example
Below is a minimal AngularJS component that demonstrates the track‑by item.id strategy and includes a shuffle function to simulate reordering.
<div ng-app="app" ng-controller="ListCtrl as ctrl">
<button ng-click="ctrl.shuffle()">Shuffle Items</button>
<ul>
<li ng-repeat="item in ctrl.items track by item.id">
{{item.id}} - {{item.name}}
</li>
</ul>
</div>
angular.module('app', [])
.controller('ListCtrl', function() {
const self = this;
// Generate 5,000 items with stable IDs
self.items = Array.from({length: 5000}, (_, i) => ({
id: i + 1,
name: 'Item ' + (i + 1)
}));
self.shuffle = function() {
// Simple Fisher–Yates shuffle
for (let i = self.items.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[self.items[i], self.items[j]] = [self.items[j], self.items[i]];
}
};
});
Validating Performance
- Open the page in Chrome, press Ctrl+Shift+I to launch DevTools.
- Navigate to the Performance panel and click Record.
- Click the Shuffle Items button a few times while recording.
- Stop the recording after a few seconds.
- In the performance summary, locate the
ngRepeatControllerorngRepeatDirectiveentry. Note the number of$digestcalls and the time spent in the repeat digest loop. - Compare these numbers against the same component rendered with:
- No
track‑by(justng-repeat="item in ctrl.items"). - With
track‑by $index(usetrack by $index).
- No
- The
track‑by item.idconfiguration should show the lowest CPU time and the fewest DOM mutations. You can also inspect the DOM tree: each<li>should retain itsdata-ng-repeat-indexvalue across shuffles, indicating node reuse.
Risk Checklist
- Verify that
item.idnever changes after the list is bound. If IDs are updated, the benefit disappears. - Ensure that the list is not shared across multiple
ng-repeatinstances that might mutate the same objects. - For older projects, consider upgrading to AngularJS 1.3+ to enable
track‑bysupport.
Takeaway
For large, dynamic lists in AngularJS, always use track‑by with a stable, unique identifier. It delivers the lowest CPU usage, minimal DOM churn, and correct rendering even when items reorder. Avoid $index unless you can guarantee that items never move, and never rely on the default behavior for performance‑critical views.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.