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.
History (last 10)
Effect Log (Auto-tracking)
An effect() logs every counter change. In zoneless mode, effects still track signal dependencies correctly.
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.
OnPushbecomes the default mental model: Components only re-render when their signal inputs change.
Migration Path
- Enable
provideExperimentalZonelessChangeDetection()inapp.config.ts - Migrate components to
signal()/computed()/effect() - Use
ChangeDetectionStrategy.OnPusheverywhere - Remove
zone.jsfrompolyfills.ts(orangular.json) - Delete any
NgZone/ChangeDetectorRefusage
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.