How To Fix Angular Change Detection Performance Issues

Change detection is one of the most powerful—but also most misunderstood—systems in Angular. When not optimized correctly, it can slow down rendering, cause unnecessary re-renders, affect scrolling, block user interactions, and degrade your overall UX.

This guide explains why change detection becomes slow and how to fix it with proven techniques.


1. What Causes Slow Change Detection?

Angular’s default strategy checks an entire component tree whenever something triggers a change such as:

  • API responses
  • Event handlers
  • Timers (setTimeout / setInterval)
  • Form updates
  • Observable emissions
  • Parent-child property changes

On large pages, this causes:

  • laggy rendering
  • slow scrolling
  • visible UI delays
  • inefficient loops

This happens because Angular uses the Default Change Detection Strategy, which checks every binding on every event.


2. Fix #1: Use OnPush Change Detection

Switching to ChangeDetectionStrategy.OnPush tells Angular:

“Re-check this component ONLY when necessary.”

How to apply:

import { Component, ChangeDetectionStrategy } from '@angular/core';

@Component({
  selector: 'app-users',
  templateUrl: './users.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class UsersComponent {
  users = [];
}

Benefits:

  • Fewer checks
  • Faster UI updates
  • Better performance in large lists
  • Works perfectly with Observables & Signals

*3. Fix #2: Use trackBy in ngFor

A huge performance mistake is:

<div *ngFor="let user of users">

Angular re-renders every item even if only one changed.

Correct version with trackBy:

<div *ngFor="let user of users; trackBy: trackByUser">
trackByUser(index: number, user: any) {
  return user.id;
}

Result:

  • Only changed rows re-render
  • Great for tables, lists, feeds

4. Fix #3: Replace Imperative Logic with Signals or Observables

Signals make change detection more predictable:

import { signal } from '@angular/core';

count = signal(0);

increase() {
  this.count.update(c => c + 1);
}

Signals update only what needs updating, not the whole view tree.


5. Fix #4: Avoid Heavy Work Inside Templates

❌ Bad:

{{ calculateSomething(bigArray) }}

This runs every cycle.

✔ Correct:

result = this.calculateSomething(bigArray);

Then use:

{{ result }}

6. Fix #5: Use Detach for Super-Complex Components

For extremely heavy components:

constructor(private cd: ChangeDetectorRef) {}

ngOnInit() {
  this.cd.detach();
}

Then manually trigger:

this.cd.detectChanges();

This gives full control over when Angular updates the component.


7. Fix #6: Avoid Multiple Async Subscriptions

Multiple nested subscriptions cause excessive updates.

❌ Bad:

service.getA().subscribe(a => {
  service.getB().subscribe(b => {
    ...
  });
});

✔ Fix using combineLatest:

combineLatest([service.getA(), service.getB()])
  .subscribe(([a, b]) => {...});

8. Fix #7: Limit DOM Size & Break Large Components

If your component template contains:

  • large tables
  • infinite loops
  • 1000+ DOM nodes

Angular change detection will lag.

Solution:

  • Break into smaller components
  • Use pagination
  • Virtual scrolling
  • Lazy loading of sub-components

9. Quick Checklist to Fix Change Detection Issues

IssueFix
UI lagSwitch to OnPush
Large list rendering slowAdd trackBy
Too many subscriptionsUse combineLatest
template calling heavy functionsMove logic to TS file
signals not usedUse signal for reactive UI
Large componentSplit into child components
SSR hydration slowReduce initial DOM load

Conclusion

Fixing Angular change detection is mainly about:

  • Updating only what needs updating
  • Reducing unnecessary checks
  • Using OnPush, trackBy, and signals
  • Structuring components efficiently

With these techniques, your Angular performance improves instantly — especially on large apps.


Citations

Internal: https://savanka.com/category/savanka-helps/
External: http://angular.dev/

Leave a Comment

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *