For years, Angular relied on Zone.js to automatically trigger change detection. It worked—but it also made performance unpredictable and debugging harder.
With modern Angular, especially alongside Signals, Angular can now run without Zone.js.
This article explains:
- what Zone.js actually did
- how Angular works without it
- what triggers change detection now
- when you should (and shouldn’t) go zoneless
What Zone.js Did in Angular
Zone.js monkey-patches async APIs like:
- setTimeout
- Promise
- fetch
- DOM events
Whenever anything async happened, Angular would:
👉 re-run change detection for the entire component tree.
Result:
✔ UI always stayed in sync
❌ Too many unnecessary checks
❌ Hard to control performance
❌ “Why did my component re-render?” moments
The Problem with Zone-Based Change Detection
With Zone.js:
- Any async event can trigger CD
- Even unrelated components re-check
- Performance tuning becomes guesswork
This was fine for small apps—but large apps paid the price.
What Is Zoneless Angular?
Zoneless Angular means:
Angular does not automatically run change detection on async events.
Instead, you explicitly tell Angular when state changes.
No magic.
No global re-checks.
Full control.
How Change Detection Works Without Zone.js
🔑 Key Concept
Change detection runs only when Angular knows data has changed.
This happens via:
- Signals
- Input changes
- Explicit triggers
Signals: The Backbone of Zoneless Angular
Example: Signal-based state
count = signal(0);
increment() {
this.count.update(v => v + 1);
}
Template:
<button (click)="increment()">+</button>
<p>Count: {{ count() }}</p>
What happens?
- Signal updates
- Angular knows exactly what changed
- Only dependent views re-render ✔ No global change detection ✔ No Zone.js involved
Computed Signals (Derived State)
doubleCount = computed(() => this.count() * 2);
Angular tracks dependencies automatically.
Only affected templates update — nothing else.
Effects (Side Effects Without Zone)
effect(() => {
console.log('Count changed:', this.count());
});
Used for:
- logging
- analytics
- syncing storage
- triggering API calls
What About Async Operations?
❌ Old Zone-based approach
setTimeout(() => {
this.value = 10;
}, 1000);
Angular had to guess when to update UI.
✅ Zoneless Approach
value = signal(0);
setTimeout(() => {
this.value.set(10);
}, 1000);
Signal update → Angular reacts → UI updates.
Running Angular Without Zone.js
Bootstrap example:
bootstrapApplication(AppComponent, {
providers: [
provideZoneChangeDetection({
eventCoalescing: true,
runCoalescing: true
})
]
});
To fully remove Zone.js:
- Remove zone.js import
- Use signals & explicit triggers
What Still Triggers Change Detection?
Even without Zone.js:
- Signal updates
- Input bindings
- Event handlers
- Manual ChangeDetectorRef calls Angular stays reactive — just not automatic everywhere.
When Zoneless Angular Shines
✅ Large applications
✅ Performance-critical UIs
✅ Apps using Signals heavily
✅ Teams that want predictability
When NOT to Go Zoneless (Yet)
⚠ Legacy apps with heavy RxJS
⚠ Apps relying on implicit async updates
⚠ Teams not ready to refactor state logic
Zoneless is opt-in, not mandatory.
Zoneless + Signals = Predictable Angular
This combo gives Angular:
- deterministic rendering
- fewer re-renders
- easier performance debugging
- modern reactive mental model
Angular becomes:
“Update only what changed — nothing else.”
Final Thoughts
Zoneless Angular is not about removing features — it’s about removing guesswork.
- Zone.js = automatic but noisy
- Zoneless + Signals = explicit and fast
You don’t need to go zoneless everywhere—but understanding it makes you a better Angular developer.
Top comments (0)