Press enter or click to view image in full size

RxJS has been around for a long time in Angular applications, which means migrating to signals can seem daunting at first. In this post, I’ll cover common patterns, tips, and tricks for migrating your application from RxJS-based to Signal-based in a step-by-step fashion.

1. Automated migrations

First and foremost, the Angular CLI supports several automated migrations, including ones that will turn your input and output decorators into Signal-friendly code, as well as ViewChild content queries.

Here are the commands you need to run:

ng generate @angular/core:signal-input-migration

ng generate @angular/core:output-migration

ng generate @angular/core:signal-queries-migration

2. Use toSignal() to convert Observables

If you’re using Reactive Forms or other APIs that return Observables, you’re one function call away from turning them into Signals:

// Before
data$ = this.http.get(this.API_URL);
// Converting to Signal
data = toSignal(this.data$);

PRO TIP: The above code will create a Signal that has an undefined value initially (Signal), which is hard to work with, as you’ll need to check for undefined values with conditions such as @if, data?.property, or data?. something ?? “defaultValue”.

You can eliminate such complexity by providing a good default value to toSignal. For instance, if you expect an array of values, a good default value would be an empty array [].

Here is an example where I expect a simple object and provide a meaningful default value instead:

// Without default value
rates: Signal = toSignal(this.rates$);
// With default value
rates: Signal = toSignal(this.rates$,
{initialValue: {USD: 1, EUR: 1, GBP: 1}}
);

Note that HTTP requests can be replaced with rxResource or httpResource (full HTTP resource tutorial here), but these APIs are still experimental at the time of this writing and thus not recommended for production use yet.

3. Subjects become Signals

We can convert Subjects to Signals using toSignal(), but removing the Subject entirely is even easier:

// Subject version
currentCurrency = new BehaviorSubject(DEFAULT_CURRENCIES[0]);
// Signal version
currentCurrency = signal(DEFAULT_CURRENCIES[0]);

4. computed() replaces RxJs operators

Almost all RxJs operators that combine Observables can be replaced with a computed signal that combines Signals.

Get Alain Chautard’s stories in your inbox

Join Medium for free to get updates from this writer.

Here is an example where I want to get the exchange rate for the currently selected currency, all of which are dynamic and can be changed asynchronously:

// RxJs code
currentExchangeRate$ = combineLatest([
this.exchangeRates$,
this.currentCurrency$
]).pipe(
map(([rates, current]) => rates[current.code as keyof ExchangeRates])
);

// Equivalent Signal based version
currentExchangeRate = computed(
() => this.exchangeRates()[this.currentCurrency().code]
);

And yes, the computed version looks much lighter than the RxJs one!

Sometimes though, we want to keep some of the RxJs logic because it works well and uses features that don’t have a direct Signal-based equivalent (here timer). In that case, toSignal does the trick to expose a Signal and “hide” our RxJs business logic from components:

// RxJs code
exchangeRates$: Observable = timer(0, 3600000)
.pipe(
switchMap(() => this.http.get(this.API_URL)),
map(data => ({
USD: 1,
EUR: data.rates.EUR,
GBP: data.rates.GBP
}))
);

// Simple Signal conversion with toSignal()
exchangeRates = toSignal(this.exchangeRates$);

For more comparisons, here is a Stackblitz of a service entirely written with Subjects and RxJs operators, and then its equivalent Stackblitz migrated to Signals.

Conclusion

Migrating from RxJS to Angular Signals isn’t a big bang rewrite — and it shouldn’t be. Angular was designed to let both coexist, and that’s a feature, not a compromise.

The pragmatic approach: migrate what’s painful first. Start with BehaviorSubject-based state in services — those are the easiest wins and the most immediate readability improvements: Services only expose signals, which simplifies your components — No more subscriptions, operators, async pipes, etc.

And remember that Signal forms are around the corner (still experimental at the time of this writing), so don’t spend too much time migrating Reactive forms just yet.

My name is Alain Chautard. I am a Google Developer Expert in Angular and a consultant and trainer at Angular Training, where I help development teams learn and become proficient with Angular / React / JavaScript.

If you need any help learning web technologies, feel free to get in touch!

If you enjoyed this article, please clap for it or share it. Your help is always appreciated. You can also subscribe to my articles and YouTube video page.

Share.
Leave A Reply