What Is Zoneless Change Detection in Angular?
Zoneless change detection is Angular 19’s new mode that runs without Zone.js. Instead of monkey-patching every async API to know when something might have changed, Angular only re-renders the components that read a Signal whose value actually changed. The result is faster rendering, simpler mental model, and no more async pipe required for state.
Why Zoneless Matters
Angular 19 lets you drop Zone.js entirely. That’s a big deal.
Zone.js is Angular’s change detection watchdog — it monkey-patches every async API (setTimeout, Promise, DOM events) to know when something might have changed, then checks the entire component tree. It works, but it’s expensive.
Going zoneless means Angular only re-renders when you explicitly tell it something changed. The result: up to 40% faster rendering in real-world apps.
Setup
Three steps. Five minutes.
1. Create a new project
ng new my-fast-app
# Choose your preferred options when prompted2. Enable zoneless change detection
import { provideExperimentalZonelessChangeDetection } from "@angular/core";
export const appConfig = {
providers: [
provideExperimentalZonelessChangeDetection(),
provideRouter(routes),
provideClientHydration(),
],
};3. Remove Zone.js from polyfills
{
"polyfills": [
// "zone.js" — remove or comment this out
]
}Verify it worked
Open your browser console and type window.Zone. If it returns undefined, Zone.js is gone.
Signals: The Replacement
Without Zone.js, Angular needs another way to know when data changes. That’s Signals — reactive primitives that notify Angular exactly which values changed and which components need updating.
Basic signal
import { Component, signal } from "@angular/core";
@Component({
selector: "app-counter",
template: `
<h2>Count: {{ count() }}</h2>
<button (click)="increment()">Add One</button>
`,
})
export class CounterComponent {
count = signal(0);
increment() {
this.count.update((n) => n + 1);
}
}Call count() to read, .set() to replace, .update() to transform. Angular tracks which templates read which signals and only re-renders those.
Computed values
Derived state that automatically stays in sync:
@Component({
template: `<p>Total: ${{ total() }}</p>`
})
export class PriceComponent {
price = signal(10);
quantity = signal(2);
total = computed(() => this.price() * this.quantity());
}total recalculates only when price or quantity change. No manual subscription management.
Effects
For side effects — things that should happen because a signal changed:
export class ThemeComponent {
theme = signal("light");
constructor() {
effect(() => {
document.body.classList.toggle("dark", this.theme() === "dark");
});
}
}RxJS interop
Already using Observables? Convert them to signals with toSignal:
@Component({
template: `<p>Time: {{ time() }}</p>`,
})
export class ClockComponent {
time = toSignal(interval(1000), { initialValue: 0 });
}Quick Reference
Frequently Asked Questions
Q1. Is Angular zoneless change detection production ready?
It is marked experimental in Angular 19 but stable enough for new projects. The API is unlikely to change significantly. For new greenfield apps it is the recommended default going forward.
Q2. What replaces Zone.js in Angular?
Signals replace Zone.js as the change detection trigger. Angular only re-renders components that read a signal whose value changed, which is faster and removes the need for the async pipe in templates.
Q3. How do I enable zoneless mode in Angular 19?
Add provideExperimentalZonelessChangeDetection() to your app providers and remove zone.js from the polyfills array in angular.json. Three lines total, no other code changes required.
Q4. Can I use Observables with Signals?
Yes. Use toSignal(observable$, { initialValue: 0 }) to bridge any RxJS stream into a signal, and toObservable(signal$) to convert back when you need to plug into an Observable-based API.
Q5. How much faster is Angular zoneless?
Real-world Angular apps see up to 40% faster rendering after switching to zoneless change detection. The exact gain depends on how much of your template tree used to be checked by Zone.js without actually needing an update.