DEV Community

ROHIT SINGH
ROHIT SINGH

Posted on

🔍 Mastering Change Detection in Angular: Best Practices for Performance Optimisation

When building scalable Angular applications, change detection plays a crucial role in maintaining performance. By default, Angular’s powerful change detection mechanism ensures that your app stays in sync with the model and view. However, if not managed effectively, it can introduce unnecessary overhead and slow down large applications.

In this blog, we’ll explore:

How Angular’s change detection works

Different strategies available

Best practices to optimize performance

Real-world examples

🚀 What is Change Detection in Angular?

Change detection is the process through which Angular updates the DOM (Document Object Model) whenever the state of the application changes.

By default, Angular uses the zone.js library to patch asynchronous operations (like setTimeout, Promise, or events). Whenever these events trigger, Angular re-renders components by running the change detection cycle.

While this ensures consistency, it can become expensive for large apps if every component gets checked unnecessarily.

⚙️ Change Detection Strategies

Angular provides two main strategies for controlling change detection:

  1. Default Strategy

Every time an event, async call, or user interaction happens, Angular checks the entire component tree.

Suitable for small to medium apps.

Can lead to performance bottlenecks in larger applications.

@Component({
  selector: 'app-user',
  templateUrl: './user.component.html',
  changeDetection: ChangeDetectionStrategy.Default
})
export class UserComponent {
  @Input() user!: User;
}
Enter fullscreen mode Exit fullscreen mode
  1. OnPush Strategy

Angular skips checking a component unless:

An @Input() property reference changes

An event originates from inside the component

You manually trigger change detection

This reduces unnecessary checks and improves performance.

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

@Component({
  selector: 'app-user',
  templateUrl: './user.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserComponent {
  @Input() user!: User;
}
Enter fullscreen mode Exit fullscreen mode

Tip: When using OnPush, always pass immutable objects or use libraries like Immer
for state updates.

🛠️ Tools to Manage Change Detection

  1. ChangeDetectorRef

Manually control change detection when needed.

detach() → Stops Angular from running change detection.

detectChanges() → Triggers detection manually.

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

@Component({
  selector: 'app-profile',
  template: ` <div>{{ counter }}</div> `
})
export class ProfileComponent {
  counter = 0;

  constructor(private cdr: ChangeDetectorRef) {}

  increment() {
    this.counter++;
    this.cdr.detectChanges(); // Trigger manually
  }
}

Enter fullscreen mode Exit fullscreen mode
  1. NgZone Optimization

Angular runs change detection for all async tasks. You can optimize it by running heavy tasks outside Angular’s zone.

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

@Component({
  selector: 'app-heavy',
  template: `<p>Heavy computation done!</p>`
})
export class HeavyComponent {
  constructor(private zone: NgZone) {
    this.zone.runOutsideAngular(() => {
      this.doHeavyTask();
      this.zone.run(() => console.log('Back to Angular zone'));
    });
  }

  doHeavyTask() {
    for (let i = 0; i < 1e9; i++) {} // Heavy loop
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. AsyncPipe

Instead of subscribing manually, use AsyncPipe in templates. It handles subscription + change detection automatically.

<div *ngIf="user$ | async as user">
  <h2>{{ user.name }}</h2>
</div>
Enter fullscreen mode Exit fullscreen mode

This avoids memory leaks and unnecessary change detection triggers.

âś… Best Practices for Efficient Change Detection

Use ChangeDetectionStrategy.OnPush wherever possible.

Prefer immutable data structures for predictable state updates.

Run heavy tasks outside Angular’s zone.

Use trackBy in *ngFor to avoid re-rendering unchanged items.

<li *ngFor="let user of users; trackBy: trackById">
  {{ user.name }}
</li>

trackById(index: number, user: any): number {
  return user.id;
}
Enter fullscreen mode Exit fullscreen mode

Leverage AsyncPipe for handling observables.

Avoid unnecessary bindings and complex expressions in templates.

Use ChangeDetectorRef.detach() for rarely updated components.

📊 Real-World Example: List Rendering

Imagine rendering a list of 10,000 users.

With Default Change Detection, Angular re-renders the entire list on each update.

With OnPush + trackBy, Angular only re-renders the updated user item, drastically improving performance.

🔑 Key Takeaway

Efficient change detection management in Angular is the key to building high-performance, scalable applications.

Use OnPush wherever possible.

Control change detection with ChangeDetectorRef.

Optimize async tasks with NgZone.

Follow best practices like AsyncPipe and trackBy.

By mastering these strategies, you’ll keep your Angular applications fast, responsive, and ready for enterprise scale.

📢 Final Thoughts

If you’re building large Angular apps, start auditing your components’ change detection strategy today. The difference in performance can be game-changing.

👉 What strategies do you use for optimizing Angular change detection? Share your thoughts in the comments!

🚀 Rohit Singh 🚀 – Medium

Read writing from 🚀 Rohit Singh 🚀 on Medium. Full-stack developer with 6+ years in Angular, Node.js & AWS. Sharing tips, best practices & real-world lessons from building scalable apps.

favicon medium.com

Top comments (0)