Going Zoneless - Part 2: Making the Move!

This is the second part of a two-part blog series: Going Zoneless! If you’re unfamiliar with the magic behind Zone.js, I recommend checking out the first post!

Advantages of Zoneless

Going Zoneless brings several advantages, including increased predictability. The magic provided by Zone.js is gone, and you now have more fine-grained control over the re-renders.

  1. Performance improvements
    • Eliminates overhead from Zone.js patching async APIs.
    • Reduced bundle size.
    • Change detection only runs when necessary.
  2. Improved debugging experience
    • Requires manual view updates, making asynchronous behavior more explicit and predictable.
  3. Better ecosystem compatibility
    • Easier integration with non-Angular libraries and third-party tools.

Note: depending on your app’s complexity and async usage, the performance improvements may be minimal.

Going Zoneless

To start the process of going Zoneless, your application must complete the following steps:

  1. Uninstall the zone.js dependency and remove its reference from the polyfills in angular.json.
    • Remove references to NgZone.onMicrotaskEmpty, NgZone.onUnstable, NgZone.isStable, & NgZone.onStable. You can explore using afterNextRender, afterRender, or the native MutationObserver when code needs to wait for a certain DOM state.
    • NgZone.run & NgZone.runOutsideAngular still work! This may be required for some libraries that still use Zone.js.
  2. Enable Zoneless by changing how you bootstrap the app:
import { provideExperimentalZonelessChangeDetection } from '@angular/core';

// standalone bootstrap
bootstrapApplication(MyApp, {
  providers: [
    provideExperimentalZonelessChangeDetection(),
    provideRouter(routes)
  ]
});

Your Angular application should now be zoneless! 🎉

Next steps

Migrate any components that use async APIs to be compatible with zoneless change detection. I strongly urge you to understand how to use signals! Updating a signal will trigger a change detection for the component.

I’ve created several before-and-after examples to demonstrate how to migrate your logic. The examples mainly highlight how going zoneless impacts ChangeDetectionStrategy.Default strategy as the migration should be fairly easy for OnPush components.

Using OnPush strategy

If you’re using OnPush strategy for your components, your application will likely work out of the box in zoneless!

Yup, you read that right! Angular now offers a more convenient way to trigger change detection using signals. When a signal updates, Angular automatically schedules change detection for the component and its subtree. No need to manually call markForCheck anymore!

HTTP Request Example

This example showcases how Zoneless is similar to using the OnPush strategy in your current app.

Notice that in the “after” example, a signal is being used. You could still use a regular variable and markForCheck.

Using Default strategy

If the component is NOT using any async APIs, no change should be required as outlined in the below example. Angular will still track variables used in the template and update the view when they change.


Closing thoughts

I believe this is a step in the right direction. Making Angular lighter, faster, and less “magical” is a win for both performance and developer experience.

In fact, I’m currently exploring how I can make carbon-components-angular support zoneless. What’s your experience been going zoneless? Do share your experience in the comments below.

See you next time! 🚀