Using ng-repeat track by to Improve Performance in AngularJS Lists
Learn how to add a track by expression to AngularJS ng-repeat to reuse DOM nodes and avoid costly rebuilds when large lists change.
09 Aug 2025, 07:59 UTC

Why track by matters
When AngularJS renders an ng-repeat it watches the collection for changes. If the array is replaced with new objects that have the same content, AngularJS treats each item as a new object because it uses the object reference as the default key. This forces the framework to destroy and recreate every DOM node, which can be expensive for large lists.
Adding a track by expression tells AngularJS how to uniquely identify each item. When the identifier stays the same, AngularJS reuses the existing DOM node and only updates the bindings that actually changed.
Worked example
<!-- controller.js -->
app.controller('ItemCtrl', function($scope) {
$scope.items = [
{ id: 1, name: 'Apple' },
{ id: 2, name: 'Banana' },
{ id: 3, name: 'Cherry' }
];
// Simulate a refresh that replaces the array with new objects
$scope.refresh = function() {
$scope.items = $scope.items.map(function(i) {
return { id: i.id, name: i.name };
});
};
});
<!-- view.html -->
Refresh list
-
{{ item.name }}
In this example the track by item.id expression uses the stable id property. When refresh() replaces the array, AngularJS sees that each id still exists and updates only the text bindings ({{ item.name }}) instead of rebuilding the <li> elements.
Limits and common mistakes
- Primitive or string result required – The expression must evaluate to a primitive (number, string) or a stringifiable value. Returning an object or array will cause AngularJS to fall back to reference comparison.
- Non‑unique keys cause duplication errors – If two items produce the same track‑by value, AngularJS throws a "Duplicates in a repeater are not allowed" error. Ensure the chosen property is truly unique.
- Avoid mutable properties – Using a property that can change during a digest cycle (e.g.,
item.selectedthat toggles) may lead to infinite loops or incorrect item associations because the key itself changes while AngularJS is trying to reconcile the list. - Fallback to $index is risky – When no stable id exists, developers sometimes use
track by $index. This works only if the list never reorders, inserts, or removes items; otherwise, AngularJS may reuse the wrong DOM node, causing visual glitches.
How to verify the effect
- Open the application in a browser and open the developer tools.
- Select the
<li>elements produced byng-repeatand note their internal IDs or properties (e.g.,data-$$hashKey). - Trigger a refresh that replaces the array but keeps the same identifiers.
- Observe that the DOM node IDs stay the same; only the text content updates.
- Optionally, add a temporary
console.loginside a custom track‑by function to confirm it is invoked only when the collection length changes.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.