In this series, you will learn:
- Why Start with Zoneless Programming?
- How to Remove Dependencies on Zone.js and Introduce Signals in Angular Projects
- Advanced Zoneless Angular Principles: Signals, Effects, and New Opportunities
- How to Properly Optimize a Zoneless Project and Monitor Its Performance
- Tips, Tricks, and the Future of Zoneless Angular: Real-World Examples
Previously, we demonstrated how manual change detection with signals works and how to transition to a zoneless architecture. Today, we take the next step by integrating signals with existing tools like Observables, cleaning up reactivity, and adopting advanced concepts introduced in Angular from version 19.
Transforming Between Signals and Observables
Signals and Observables share a similar principle of reactivity but operate differently. Signals are synchronous and react immediately to changes, while Observables enable asynchronous data processing. Combining these two opens the door to more robust and flexible Angular application designs.
Connecting signals with Observables is straightforward, using the toSignal and toObservable methods. Here’s an example where the patientId signal drives asynchronous data loading via Observables:

Explanation:
- toObservable converts a signal into an Observable, which can be processed with RxJS operators (in this case, switchMap).
- toSignal then converts the Observable’s output back into a signal.
- initialValue ensures the signal always has a value, even if the Observable has not returned any data yet.
This approach combines the best of both worlds—the simplicity and immediate reactivity of signals with the asynchronous capabilities of Observables.
However, for the application to remain reliable and efficient in the long term, attention must be paid to properly cleaning up reactive elements and managing their lifecycle.
Cleaning Signals and Effects
Every reactive system has its lifecycle management rules. If signals or effects are not properly terminated, they can cause issues like retaining references to non-existent components. These “zombie” references can lead to memory leaks, unpredictable application behavior, or even crashes. Cleanup is therefore essential to maintain performance, stability, and prevent unexpected errors.
Most cleanup in Angular happens automatically if signals and effects are correctly defined within the context of a component, directive, or service. Angular ensures their automatic disposal during the destroy process. Manual cleanup is only required when signals or effects are defined outside the Angular Dependency Injection context (e.g., outside a class constructor).
Terminating Effects
Effects (effect) allow you to track and respond to signal changes.
Manually Terminating an Effect
Every effect returns a reference that can be terminated using the destroy() method:

This ensures the effect no longer reacts to signal changes or executes its logic.
Alternative: Manual Cleanup with manualCleanup
If you want even more control over the lifecycle of effects, you can enable manual cleanup using the manualCleanup setting:

This approach allows developers to precisely determine when and how an effect is terminated, providing maximum flexibility. Such manual interventions are only necessary in specific cases outside the context of components or services.
The best practice when working with signals and effects is to define them directly within the context of a component, directive, or service. Angular automatically manages their lifecycle and cleanup, eliminating the need for manual interventions. Manual cleanup and injector use are exceptional scenarios for signals or effects that cannot be managed in the standard way.
Using the Injector to Manage Signals Outside Components
The Angular injector ensures that signals and effects have access to all necessary dependencies and the lifecycle of the Angular application. This mechanism is essential for properly integrating signals across various parts of the application.

With the growing support for signals in Angular, enhancements are simplifying framework usage. These include gradual replacements for traditional annotations with modern signal alternatives, offering greater flexibility and more intuitive reactivity management.
Replacing Built-In Annotations with Signals
Angular is extending the signal architecture to replace traditional annotations like @Input and @Output. This approach improves code readability, maintainability, and simplifies communication between components. Signal alternatives enable more precise and efficient data change management in applications.
Replacing @Input with Signals
Using signals as a replacement for @Input allows developers to easily read values passed into a component and react to their changes immediately.

This signal-based alternative eliminates the need for traditional input annotations and allows straightforward derivation of values using computed.
Replacing @Output with Signals
Replacing @Output with a signal-based alternative provides better control over component events. Instead of traditional EventEmitters, signals enable more efficient communication between components.

This approach simplifies the code and offers a modern way to handle events that integrates seamlessly into the signal architecture.
Model: Two-Way Communication
The model acts as a Swiss Army knife for two-way communication between components. It combines the functionality of @Input and @Output into a single property, reducing code complexity and simplifying the management of values that need to be both tracked and modified.
However, its versatility can be a double-edged sword. While the model provides great flexibility, it should not be used indiscriminately for all inputs. It can expose too much functionality, potentially complicating code readability and management. Therefore, model signals are best utilized in scenarios where two-way communication offers real value, such as forms or complex interactions between parent and child components.

Using model signals eliminates the need for separate handling of input and output values, as one property can cover both.
Beyond inputs and outputs, Angular also focuses on other areas of reactivity, introducing signal alternatives for working with the DOM tree, simplifying the manipulation of nested components and content.
View and Content Queries: New Signal Alternatives
Signal replacements for annotations like @ViewChild, @ViewChildren, @ContentChild, and @ContentChildren enable more efficient management of dependencies between components. These alternatives make it easy to work with nested components and derive their state without manual change tracking.
Example: Replacing @ViewChildren

This signal alternative allows deriving values or tracking changes in nested components without complex code interventions. The result is simpler and more transparent reactivity management in the application.
Lifecycle Hooks and Signals
With signal replacements for @Input, @Output, and transformations in view and content queries, it’s logical that traditional lifecycle hooks like ngOnChanges, ngAfterContentInit, and ngAfterViewInit, are becoming obsolete.
All external writes and mutations outside signals are easily managed with effects, afterRender, and afterNextRender, defined directly in the component class constructor.
Advanced Reactivity Options
As if that weren’t enough, the recently released Angular 19 introduces experimental implementations of two new signal enhancements for even better migration to a zoneless architecture: linkedSignal and resource.
linkedSignal

The linkedSignal depends on another signal similarly to computed, but allows dynamic value writing. This eliminates the need to create duplicate signals or rewrite values through effects.
Resource: Reactivity for Asynchronous Operations
The new Angular takes reactivity a step further with the resource tool, offering a novel way to handle asynchronous operations. Initially, its purpose was somewhat unclear, but it has proven to bring significant value to Angular’s signal architecture.
Resource is primarily used for handling asynchronous calls, such as HTTP requests. This mechanism allows signals to be tracked and respond to their changes through asynchronous operations. As a result, it can replace the need for HttpClient and RxJS streams while maintaining a reactive programming style.
When a signal associated with a resource changes, an asynchronous function is triggered to process the new data. This process is fully integrated into the signal architecture, enabling efficient call management and response to changes without manual control.

This tool is particularly useful when working with server data, where simplifying asynchronous call management while leveraging the benefits of signal reactivity is essential.
Gradual Migration to Zoneless Applications
Fully transforming an application to signal architecture is a complex process that cannot be achieved instantly, especially for large projects. Angular provides efficient tools and strategies to help developers transition gradually and safely.
For existing projects, a gradual migration approach is essential. Instead of attempting to completely remove Zone.js all at once, it is better to start introducing signals into new components and progressively transform older parts of the application.
Tools to Facilitate Migration
Angular provides several tools to simplify and automate this process:
- CLI Rules and Migrators: Angular CLI gradually offers support for automatic code transformations. These tools allow signals, new annotations, and other parts of the signal architecture to be introduced faster and with fewer errors.
- ESLint Rules: Angular has extended ESLint rules to help identify areas where signals can or should be introduced. These rules simplify migration by directing developers to the necessary adjustments.
- Hybrid Solutions: In hybrid applications where signals are combined with RxJS, it is sometimes necessary to use markForCheck or, in the worst case, DetectChanges. These solutions ensure compatibility during the transitional period.
Looking Ahead
Angular continues to expand and improve tools for the signal architecture, with some significant new features already coming with stable support in Angular 19. Key improvements include:
- Support for Query Lists and Lazy Loading within the signal architecture, making it easier to work with dynamic data.
- Migrating core framework mechanisms, such as inputs, outputs, and signal alternatives for the router, creating modern solutions for more efficient reactivity.
Transitioning to a signal architecture represents a significant step in building modern applications. With Angular 19, you can fully leverage the stable support of these technologies, making the benefits of signals and zoneless programming immediately accessible. This gradual approach is ideal for team-based work on complex projects as it provides tools that facilitate transformation without disrupting functionality.
Adopting the signal architecture brings not only better performance and cleaner code but also new opportunities for application optimization and growth. With stable foundations, you can progressively implement modern principles of reactivity, taking your projects to the next level.
What’s Next?
In the next blog, we will focus on performance optimization in zoneless Angular applications. We will explore how to use standalone components, lazy loading, and render control to boost efficiency. Additionally, we’ll look at debugging and performance monitoring without automatic change detection. Stay tuned!