DEV Community

Manthan Ankolekar
Manthan Ankolekar

Posted on

Generating a Zoneless Angular App

Why Use Zoneless?

Removing ZoneJS from an Angular application offers several advantages:

  • Improved performance: ZoneJS frequently triggers application synchronization, even when the state hasn't changed.
  • Better Core Web Vitals: Eliminating ZoneJS reduces payload size and startup time.
  • Easier debugging: ZoneJS makes stack traces harder to interpret.
  • Enhanced ecosystem compatibility: Some browser APIs and third-party libraries are incompatible with ZoneJS, making maintenance more complex.

Enabling Zoneless in an Application

The API for zoneless mode is experimental and subject to change. To enable zoneless mode:

Standalone Bootstrap

bootstrapApplication(MyApp, {providers: [
  provideExperimentalZonelessChangeDetection(),
]});
Enter fullscreen mode Exit fullscreen mode

NgModule Bootstrap

platformBrowser().bootstrapModule(AppModule);

@NgModule({
  providers: [provideExperimentalZonelessChangeDetection()]
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

Removing ZoneJS

To remove ZoneJS from an Angular project:

  • Remove zone.js and zone.js/testing from the angular.json polyfills.

  • If using polyfills.ts, delete:

   import 'zone.js';
   import 'zone.js/testing';
Enter fullscreen mode Exit fullscreen mode
  • Uninstall ZoneJS from dependencies:
   npm uninstall zone.js
Enter fullscreen mode Exit fullscreen mode

Requirements for Zoneless Compatibility

Angular determines when to run change detection through specific notifications, such as:

  • ChangeDetectorRef.markForCheck (automatically triggered by AsyncPipe)
  • ComponentRef.setInput
  • Updating a signal referenced in a template
  • Template event listeners
  • Attaching views marked as dirty

OnPush-Compatible Components

Using ChangeDetectionStrategy.OnPush is recommended for compatibility. While not mandatory, it ensures Angular efficiently detects updates without relying on ZoneJS.

Removing Incompatible NgZone Features

The following APIs do not work in zoneless mode and should be removed:

  • NgZone.onMicrotaskEmpty
  • NgZone.onUnstable
  • NgZone.isStable
  • NgZone.onStable

Replace them with:

  • afterNextRender() for single change detection waiting
  • afterRender() for conditions spanning multiple cycles
  • MutationObserver for direct DOM state monitoring

Note: NgZone.run and NgZone.runOutsideAngular are still supported.

PendingTasks for Server-Side Rendering (SSR)

ZoneJS helps determine when SSR should serialize the page. Without ZoneJS, use the PendingTasks service to track async tasks:

const taskService = inject(PendingTasks);
const taskCleanup = taskService.add();
await doSomeWorkThatNeedsToBeRendered();
taskCleanup();
Enter fullscreen mode Exit fullscreen mode

Angular uses this internally for routing and HTTP requests.

Testing and Debugging

Using Zoneless in TestBed

To test a zoneless Angular app:

TestBed.configureTestingModule({
  providers: [provideExperimentalZonelessChangeDetection()]
});
const fixture = TestBed.createComponent(MyComponent);
await fixture.whenStable();
Enter fullscreen mode Exit fullscreen mode

Avoid fixture.detectChanges() to mimic real-world change detection.

Debug Mode for State Updates

Use provideExperimentalCheckNoChangesForDebug() to catch undetected updates. Angular will throw ExpressionChangedAfterItHasBeenCheckedError if a binding changes without notification.

By following these steps, you can successfully migrate your Angular app to a zoneless architecture, improving performance and maintainability.

Feel free to explore the GitHub repository for more details and to try out the project yourself. Happy coding!

Top comments (0)