Performance problems in Angular apps are frequently caused by the way the framework refreshes the DOM and manages state changes. NgZone and ChangeDetectorRef (CDR) are two essential technologies used in this approach. Building responsive and effective Angular apps requires a grasp of how these APIs complement one another and when to utilize them, even though developers often come across them in performance optimization guidelines.
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 (setTimeout and setInterval)
- 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() or markForCheck().
How NgZone and CDR Cooperate
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:
- Zone.js ignores each timer tick.
- Angular does not perform global change detection every 100 milliseconds.
3. Perform Background Processing
this.rawTicks++;
this.currentPrice +=
(Math.random() - 0.5) * 20;
The application continues processing incoming data without updating the DOM on every tick.
4. Update the UI Only When Needed
if (this.rawTicks % 10 === 0) {
this.cdr.detectChanges();
}
Instead of updating the UI every 100 milliseconds, the component refreshes only once every second.
This significantly reduces rendering work while keeping the displayed data current.
How NgZone and ChangeDetectorRef Work Together
These APIs solve different problems but complement one another.
NgZone.runOutsideAngular()
- Prevents unnecessary global change detection.
- Ideal for high-frequency background operations.
- Reduces CPU usage by isolating expensive asynchronous work.
ChangeDetectorRef.detectChanges()
- Performs change detection only for the current component and its children.
- Leaves the rest of the application untouched.
- Provides precise control over when the UI is updated.
NgZone vs. ChangeDetectorRef
| Feature | NgZone | ChangeDetectorRef |
| Primary purpose |
Controls when Angular runs change detection |
Controls how and where change detection runs |
| Scope |
Entire Angular application |
Individual component |
| Typical use case |
High-frequency asynchronous operations |
Manual UI updates |
| Common methods |
run(), runOutsideAngular() |
detectChanges(), markForCheck(), detach(), reattach() |
| Performance benefit |
Prevents unnecessary global checks |
Updates only targeted components |
When Should You Use NgZone?
Use NgZone when working with:
- WebSocket streams
- Timers
- Canvas animations
- High-frequency sensor data
- Third-party JavaScript libraries
- Background processing
- Continuous polling
The goal is to prevent Angular from reacting to every asynchronous event.
When Should You Use ChangeDetectorRef?
Use ChangeDetectorRef when you need:
- Manual UI updates
- Better control with OnPush
- Selective component rendering
- Detached component trees
- Performance optimization for complex views
- It provides fine-grained control over Angular's rendering behavior.
Best Practices
For performance-sensitive Angular applications:
- Use ChangeDetectionStrategy.OnPush whenever appropriate.
- Execute high-frequency background work outside Angular's zone.
- Use markForCheck() when the component should be refreshed during the next change detection cycle.
- Use detectChanges() when an immediate UI update is required.
- Avoid triggering unnecessary application-wide change detection.
Combining runOutsideAngular() with targeted ChangeDetectorRef updates is one of the most effective optimization techniques available in Angular.
Conclusion
Although NgZone and ChangeDetectorRef are closely related to Angular's change detection mechanism, they serve different purposes. NgZone determines when Angular should perform change detection by monitoring asynchronous operations, while ChangeDetectorRef determines how and where component views are updated.
Using them together enables Angular applications to process high-frequency background operations efficiently while minimizing unnecessary DOM updates. This approach improves responsiveness, reduces CPU usage, and helps build scalable, high-performance applications.