Zoneless Angular Explained — How Change Detection Works Without Zone.js

Published: (January 9, 2026 at 01:06 PM EST)
3 min read
Source: Dev.to

Source: Dev.to

Introduction

For years Angular relied on Zone.js to automatically trigger change detection. It kept the UI in sync, but it also made performance unpredictable and debugging harder. Modern Angular—especially with the introduction of Signals—can now run without Zone.js.

What Zone.js Actually Did

Zone.js monkey‑patches asynchronous APIs such as:

  • setTimeout
  • Promise
  • fetch
  • DOM events

Whenever any async operation occurred, Angular would run change detection automatically. This meant:

  • UI always stayed in sync.
  • Any async event could trigger change detection, even for unrelated components.
  • Performance tuning became guesswork, especially in large applications.

How Angular Works Without Zone.js

When Zone.js is removed, Angular no longer runs change detection automatically on every async event. Instead, you explicitly tell Angular when the application state has changed. Change detection runs only when Angular knows data has changed, via:

  • Signals
  • Input property changes
  • Explicit triggers (e.g., ChangeDetectorRef)

Signals Overview

Signals are a reactive primitive that let Angular track dependencies automatically.

// signal.ts
import { signal, computed, effect } from '@angular/core';

export class CounterComponent {
  count = signal(0);

  increment() {
    this.count.update(v => v + 1);
  }

  doubleCount = computed(() => this.count() * 2);

  constructor() {
    effect(() => {
      console.log('Count changed:', this.count());
    });
  }
}
Count: {{ count() }}
Double: {{ doubleCount() }}
  • Signal updates: Angular knows exactly what changed, so only dependent views re‑render.
  • No global change detection: No Zone.js involvement.

Comparing the Old Zone‑Based Approach with the Zoneless Approach

Zone‑Based Example

// old.component.ts
setTimeout(() => {
  this.value = 10;   // Angular had to guess when to update the UI
}, 1000);

Zoneless Example Using Signals

// new.component.ts
value = signal(0);

setTimeout(() => {
  this.value.set(10);   // Signal update → Angular reacts → UI updates
}, 1000);

Bootstrapping Without Zone.js

import { bootstrapApplication } from '@angular/platform-browser';
import { provideZoneChangeDetection } from '@angular/core';

bootstrapApplication(AppComponent, {
  providers: [
    provideZoneChangeDetection({
      eventCoalescing: true,
      runCoalescing: true
    })
  ]
});

To fully remove Zone.js:

  1. Delete the zone.js import from your project.
  2. Use signals and explicit triggers for state changes.
  3. Optionally call ChangeDetectorRef methods for manual change detection.

Even without Zone.js, Angular remains reactive through:

  • Signal updates
  • Input bindings
  • Event handlers
  • Manual ChangeDetectorRef calls

When to Go Zoneless

  • Large applications: Deterministic rendering and fewer unnecessary re‑renders make performance debugging easier.
  • Legacy apps with heavy RxJS usage: Consider the trade‑offs; Zoneless is opt‑in, not mandatory.

Benefits of the Zoneless + Signals Model

  • Deterministic rendering – updates occur only where data actually changed.
  • Fewer re‑renders – improves runtime performance.
  • Easier performance debugging – no hidden Zone.js side effects.
  • Modern reactive mental model – aligns with functional reactive programming concepts.

Conclusion

Zoneless Angular isn’t about stripping features; it’s about removing guesswork.

  • Zone.js provides automatic but noisy change detection.
  • Signals + explicit triggers give you explicit, fast, and predictable updates.

You don’t need to go completely zoneless everywhere, but understanding how it works makes you a better Angular developer.

Back to Blog

Related posts

Read more »