Slow performance in Angular applications is often caused by inefficient change detection, especially when the app grows large or when many components re-render unnecessarily. Angular’s default change detection strategy checks every component on every event, which can slow down the UI if not handled properly.
This guide explains the causes of slow change detection and provides practical fixes used by modern Angular developers.
1. What Causes Slow Change Detection?
Angular runs change detection whenever events occur, such as:
- Clicks, keypresses, scrolls
- HTTP calls
- Timers (setTimeout, intervals)
- Observable emissions
- Change in input bindings
When these are not optimized, Angular repeatedly checks the entire component tree, causing:
- Slow UI updates
- Janky scrolling
- Delayed DOM updates
- Freezing in large lists
2. Fix #1: Use OnPush Change Detection Strategy
The default change detection checks everything.
Switching to ChangeDetectionStrategy.OnPush tells Angular to check a component only when needed.
How to apply:
import { Component, ChangeDetectionStrategy } from '@angular/core';
@Component({
selector: 'app-user-list',
templateUrl: './user-list.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserListComponent {
// component logic
}
Benefits:
- Up to 40–60% faster rendering
- Components update only when:
- Input changes
- Event triggered inside component
- Observable emits new value
- Manually marked for check
*3. Fix #2: Always Use trackBy With ngFor
Large lists cause unnecessary DOM re-renders.
<div *ngFor="let user of users; trackBy: trackById">
{{ user.name }}
</div>
trackById(index: number, item: any) {
return item.id;
}
Why this fixes lag?
- Without
trackBy, Angular destroys and re-creates DOM elements - With
trackBy, Angular updates only the changed item
4. Fix #3: Move Heavy Logic Out of Templates
Avoid expensive operations directly inside templates:
❌ Bad example:
<div>{{ calculateTotal() }}</div>
This runs on every change detection cycle.
✅ Fix:
Run it once in TS and expose a variable:
total = this.calculateTotal();
5. Fix #4: Convert Data Streams to signals (Angular 16+)
Signals reduce unnecessary re-renders.
Example:
const counter = signal(0);
counter.update(v => v + 1);
Signals notify Angular only when actual data changes, not on every event.
6. Fix #5: Avoid Change Detection Triggers in Loops
Common mistake:
setInterval(() => {
this.value++;
}, 10);
This triggers change detection 100 times per second.
Fix:
Run timers inside NgZone.runOutsideAngular():
this.ngZone.runOutsideAngular(() => {
setInterval(() => {
this.ngZone.run(() => this.value++);
}, 1000);
});
7. Fix #6: Use Immutable Data Structures
Immutable changes help Angular detect updates faster:
this.users = [...this.users, newUser];
Instead of mutating arrays:
this.users.push(newUser); // ❌ triggers full re-check
8. Fix #7: Break Down Large Components
If one giant component handles:
- API calls
- Nested UI
- Forms
- Lists
- Business logic
…it will cause frequent change detection cycles.
Solution:
Split it into smaller feature components with OnPush strategy.
9. Fix #8: Use Pure Pipes Instead of Methods in Templates
Angular executes template methods often.
Replace methods with pure pipes that execute only when input changes.
Final Thoughts
Most Angular performance problems are solvable with:
- OnPush strategy
- trackBy on lists
- Immutable data handling
- Signals
- Template optimization
- Smaller, smarter components
Once these are applied, change detection becomes predictable, faster, and scalable for complex apps.
Citations
Internal: https://savanka.com/category/savanka-helps/
External: http://angular.dev/