Zoneless Change Detection

This page demonstrates Angular's experimental zoneless change detection, bootstrapped with provideExperimentalZonelessChangeDetection().

Signal-Driven Counter (No zone.js)

This counter uses signal(), computed(), and effect() — all working without zone.js patching.

Current Value
0
Doubled: 0 · Parity: even
Async Update Test (setTimeout)
History (last 10)

Effect Log (Auto-tracking)

An effect() logs every counter change. In zoneless mode, effects still track signal dependencies correctly.

Effect Log
No entries — interact with the counter above.

What Zoneless Mode Changes

No zone.js Required

The zone.js library (~30KB minzipped) is no longer loaded. Angular's signals system directly notifies the framework when state changes, eliminating the need for zone.js's monkey-patching of browser APIs (setTimeout, Promise, addEventListener, etc.).

Bundle Size Reduction

Removing zone.js reduces the initial bundle by ~30KB. For apps using signals + OnPush everywhere, this is pure savings with no runtime cost.

Performance: No Zone Overhead

zone.js wraps every async task to trigger change detection. Zoneless removes this overhead entirely. Change detection runs only when signals actually change — no more global zone checks on every setTimeout, Promise resolution, or DOM event.

Developer Experience Changes

  • No more NgZone.run() / runOutsideAngular(): Signals work everywhere. Async callbacks update UI automatically.
  • No ChangeDetectorRef.detectChanges(): Signal mutations trigger updates directly.
  • Third-party libs work out of the box: Libraries that don't trigger zone.js (e.g., some WebSocket libs, canvas animations) now update UI correctly when using signals.
  • OnPush becomes the default mental model: Components only re-render when their signal inputs change.

Migration Path

  1. Enable provideExperimentalZonelessChangeDetection() in app.config.ts
  2. Migrate components to signal() / computed() / effect()
  3. Use ChangeDetectionStrategy.OnPush everywhere
  4. Remove zone.js from polyfills.ts (or angular.json)
  5. Delete any NgZone / ChangeDetectorRef usage

Current App Configuration

import { ApplicationConfig, provideExperimentalZonelessChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';

export const appConfig: ApplicationConfig = {
  providers: [
    provideExperimentalZonelessChangeDetection(),
    provideRouter(routes),
    provideClientHydration(),
  ],
};

Signal Inputs (Angular 17.1+)

Zoneless works seamlessly with input() signals. Parent updates propagate directly without zone.js.

Child received (via signal input):
0
Doubled in child: 0

HTTP Request Log

0 pending | 0 ok | 0 error
No HTTP requests logged yet