Introduction
In Angular applications, performance issues often stem from how the framework handles state changes and updates the DOM. Two fundamental tools behind this process are NgZone and ChangeDetectorRef (CDR).
Although developers frequently encounter these APIs in performance optimization guides, understanding how they complement each other—and when to use each—is essential for building responsive and efficient Angular applications.
The Roles: "When" vs. "How"
To understand their relationship, think of Angular's rendering engine as a two-stage pipeline.
NgZone: Controls When Change Detection Runs
NgZone monitors asynchronous operations such as:
DOM events
HTTP requests
Timers (
setTimeoutandsetInterval)Promises
User interactions
Using Zone.js, Angular detects when an asynchronous task completes. If the task runs inside the Angular zone, Angular automatically triggers a global change detection cycle across the application.
ChangeDetectorRef: Controls How and Where Change Detection Runs
ChangeDetectorRef provides manual control over a component's view.
It allows you to:
Mark a component for checking using
markForCheck()Trigger an immediate change detection cycle using
detectChanges()Detach a component from Angular's change detection tree
Reattach a previously detached component
Unlike NgZone, which influences the entire application, ChangeDetectorRef focuses on a specific component.
Real-World Scenario: A High-Frequency Live Data Ticker
Imagine you're building a financial dashboard that receives stock or cryptocurrency prices every 100 milliseconds through a WebSocket or timer.
The Problem
If these updates are processed inside Angular's default zone, every update triggers a full application-wide change detection cycle.
With updates arriving every 100 milliseconds:
Angular performs approximately 10 change detection cycles per second.
CPU usage increases.
UI responsiveness decreases.
Input lag and animation stuttering become noticeable.
The Solution
A more efficient approach is to:
Execute the high-frequency background work outside Angular's zone using
NgZone.runOutsideAngular().Update the UI only when necessary using
ChangeDetectorRef.detectChanges()ormarkForCheck().

Example: Optimizing a Live Price Ticker
import {
Component,
OnInit,
OnDestroy,
ChangeDetectionStrategy,
ChangeDetectorRef,
NgZone
} from '@angular/core';
@Component({
selector: 'app-live-ticker',
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div class="ticker-card">
<h2>Live Bitcoin Price</h2>
<p class="price">
{{ currentPrice | currency:'USD' }}
</p>
<small>
Background Processing Ticks: {{ rawTicks }}
</small>
</div>
`
})
export class LiveTickerComponent implements OnInit, OnDestroy {
currentPrice = 50000;
rawTicks = 0;
private timerId: any;
constructor(
private ngZone: NgZone,
private cdr: ChangeDetectorRef
) {}
ngOnInit(): void {
this.ngZone.runOutsideAngular(() => {
this.timerId = setInterval(() => {
this.rawTicks++;
this.currentPrice += (Math.random() - 0.5) * 20;
if (this.rawTicks % 10 === 0) {
this.cdr.detectChanges();
}
}, 100);
});
}
ngOnDestroy(): void {
if (this.timerId) {
clearInterval(this.timerId);
}
}
}
Code Breakdown
1. Use OnPush Change Detection
changeDetection: ChangeDetectionStrategy.OnPush
This prevents Angular from automatically checking the component during every global change detection cycle.
Instead, updates occur only when:
An input changes
An event occurs
The component is explicitly marked for checking
detectChanges()is called
2. Execute Background Work Outside Angular
this.ngZone.runOutsideAngular(() => {
...
});
The timer executes outside Angular's zone.
As a result:

Join the conversation! Your thoughts help the community grow.