Back to blog
Miloš Krstić3 min read

Migrating to Signals: A Step-by-Step Performance Case Study

Migrating to Signals: A Step-by-Step Performance Case Study
Click image to enlarge

A Journey from Zone.js to Signals

For Angular developers, reactivity has been synonymous with Zone.js for years. This library enabled "magical" UI updates by monkey-patching almost all asynchronous operations in the browser. While this facilitated initial development, the cost was high: unpredictable change detection cycles and difficult debugging.

Today, with the stabilization of the Zoneless mode in versions 20 and 21, Angular is transitioning to a precise, graph-based reactivity model using Signals.

Why Are We Leaving Zone.js?

The main issue with Zone.js is that it knows something happened, but it doesn't know what changed. Because of this, Angular has to traverse the entire component tree (from top to bottom) to check where the change occurred.

In enterprise applications with hundreds of modules, this leads to:

  • Performance overhead: Unnecessary detection cycles that consume CPU.
  • Larger bundles: Zone.js adds about 33KB to your application, directly impacting Core Web Vitals.
  • Lack of transparency: Error stack traces are often buried under internal Zone.js library frames.

Signals: Fine-Grained Reactivity

Signals shift the paradigm from "push changes everywhere" to "track only what is used" (pull-based reactivity).

The main building blocks are:

  1. signal(): Your primary source of state.
  2. computed(): Derived states that are lazy and memoized (calculated only when needed and when dependencies change).
  3. effect(): Used for side effects like logging or manual DOM synchronization.

This approach enables Angular to perform surgically precise updates. Instead of scanning the entire tree, it only updates the part of the template that directly depends on the changed signal.

The New Standard for Components

The migration to signals brings entirely new functional APIs that replace legacy decorators:

Legacy API New Signal API Advantage
@Input() input() Read-only signal, supports built-in transformations
@Output() output() Cleaner syntax, no longer requires EventEmitter
@ViewChild() viewChild() Reactive queries, no more undefined errors before ngAfterViewInit

The Path to Zoneless Stability

If you want to extract maximum performance (and achieve 100/100 on Lighthouse), Zoneless Angular is the goal. Since zoneless change detection is now the default behavior in modern Angular, here is what the process looks like in two steps:

  1. Explicit Reactivity: Every state change must happen through signals or the AsyncPipe. "Accidental" UI refreshes are a thing of the past.
  2. Dropping the Weight: Remove zone.js from the polyfills section in angular.json and uninstall the package entirely.

The result? Benchmarks show 50–70% faster change detection and up to 35% fewer re-renders compared to the traditional RxJS approach in enterprise systems.

What About RxJS?

Signals do not replace RxJS. They are built for UI state, while RxJS remains unparalleled for asynchronous orchestration (HTTP calls, WebSockets, complex streams). You should use @angular/core/rxjs-interop (functions like toSignal and toObservable) to seamlessly bridge these two worlds.

Conclusion

Moving from Zone.js to Signals is not just a cosmetic code update. It is a fundamental shift toward an architecture that is faster, easier to debug, and prepared for the future of the web. For enterprise applications, this is the difference between a system that noticeably lags and one that scales without effort.

Technical implementation notes:

  • Testing: In a zoneless environment, fakeAsync no longer works. Migrate to native async/await and fixture.whenStable().
  • Debugging: Use provideCheckNoChangesConfig({exhaustive: true}) during development to catch state changes that Angular failed to detect.

Struggling with a similar frontend issue?

Get a Frontend Diagnosis
  • Identify performance bottlenecks
  • Spot architectural issues
  • Get actionable recommendations
Audit Frontend Architecture