Angular 19 brings some exciting improvements—Event Replay, zoneless states, and most notably, linkedSignal
. Of all the new features, linkedSignal
stands out as a clear evolution of Angular’s reactivity model. It’s fast, intuitive, and drastically cuts down boilerplate while boosting performance. To me, it represents peak Angular—powerful, performant, and simple to use.
If you’re already using computed
signals, linkedSignal
is the upgrade you didn’t know you needed.
💡 Need help upgrading to Angular 19 or modernizing your app with linkedSignal? Bitovi’s Angular consultants can guide you through it, step by step.
Before linkedSignal
: RxJS Overhead
Before signals, reactive state in Angular meant RxJS. Whether using BehaviorSubject
, Subject
, or Observable
pipelines, you had to manage subscriptions, handle teardown logic, and write a lot of verbose code.
promoCodeInput$ = new BehaviorSubject<string>('');
promoMessage$ = new BehaviorSubject<PromoMessage>('Enter a promo code.');
errorMessage$ = new Subject<PromoMessage | null>();
RxJS is powerful, but it’s not always lightweight. When it comes to UI reactivity, the new signal-based approach provides a simpler and more performant alternative.
Why linkedSignal
?
linkedSignal
fills a long-missing gap in Angular's signal system: writable computed values. While computed()
lets you derive values reactively, it’s read-only. With linkedSignal
, you get a two-way reactive binding that can both derive and update state.
This makes linkedSignal
especially useful for scenarios like form inputs, validation logic, or any UI that requires bidirectional data flow. It’s declarative, clean, and easier to reason about.
Plus, it handles dependency tracking for you—no need to manually optimize chains of derived values. That means fewer bugs, less boilerplate, and more maintainable code.
RxJS Example
Here’s what a typical reactive promo code form might look like with RxJS:
@Component({
selector: 'pmo-restaurant',
template: `
<div>
<p>Enter Promo Code:</p>
<input [value]="promoCodeInput$.value" (input)="onPromoCodeInput($event)" />
<p>{{ promoMessage }}</p>
<button (click)="submitOrder()">Submit Order</button>
</div>
`,
})
export class PromoCheckoutComponent implements OnDestroy {
validPromoCodes = ['DISCOUNT10', 'SAVE20'];
promoCodeInput$ = new BehaviorSubject<string>('');
promoMessage$ = new BehaviorSubject<PromoMessage>('Enter a promo code.');
errorMessage$ = new Subject<PromoMessage | null>();
promoMessage: PromoMessage = 'Enter a promo code.';
private readonly destroy$ = new Subject<void>();
constructor() {
combineLatest([
this.promoCodeInput$,
this.errorMessage$.pipe(map((msg) => msg || null))
])
.pipe(
map(([code, errorMsg]) => {
if (errorMsg) return errorMsg;
if (!code) return 'Enter a promo code.';
return this.validPromoCodes.includes(code)
? 'Promo code applied successfully!'
: 'Invalid promo code.';
}),
tap((message) => (this.promoMessage = message)),
takeUntil(this.destroy$)
)
.subscribe();
}
onPromoCodeInput(event: Event) {
const input = (event.target as HTMLInputElement).value;
this.promoCodeInput$.next(input);
this.errorMessage$.next(null);
}
submitOrder() {
const code = this.promoCodeInput$.value;
if (!this.validPromoCodes.includes(code)) {
this.errorMessage$.next('Submission failed. Invalid promo code.');
} else {
alert('Order submitted successfully!');
}
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}
linkedSignal
Version
Now compare that to the same logic using linkedSignal
:
@Component({
selector: 'pmo-restaurant',
template: `
<div>
<p>Enter Promo Code:</p>
<input [value]="promoCodeInput()" (input)="onPromoCodeInput($event)" />
<p>{{ promoMessage() }}</p>
<button (click)="submitOrder()">Submit Order</button>
</div>
`,
})
export class PromoCheckoutComponent {
validPromoCodes = ['DISCOUNT10', 'SAVE20'];
promoCodeInput = signal('');
promoMessage = linkedSignal<PromoMessage>(() => {
const code = this.promoCodeInput();
if (!code) return 'Enter a promo code.';
return this.validPromoCodes.includes(code)
? 'Promo code applied successfully!'
: 'Invalid promo code.';
});
onPromoCodeInput(event: Event) {
const input = (event.target as HTMLInputElement).value;
this.promoCodeInput.set(input);
}
submitOrder() {
if (!this.validPromoCodes.includes(this.promoCodeInput())) {
this.promoMessage.set('Submission failed. Invalid promo code.');
} else {
alert('Order submitted successfully!');
}
}
}
No teardown logic. No subscriptions. Just clear, readable, and efficient reactive state.
Final Thoughts
linkedSignal
is one of the most practical additions to Angular’s reactivity model. It simplifies common UI patterns, removes RxJS overhead for many use cases, and results in cleaner code.
In short: it just works. It’s simple, powerful, and feels like the natural next step for anyone already using signals. I’ve been impressed with how much friction it removes from reactive state management—and I think you will be, too.
Angular 19 is pushing the framework toward a lighter, more ergonomic development model. linkedSignal
is a big step forward—and it’s one you don’t want to miss.
🚀 Ready to modernize your Angular app or plan a smooth upgrade path to Angular 19? Let Bitovi help you get there. Our team of Angular consultants has helped Fortune 500s and startups alike adopt Angular best practices and accelerate releases. Schedule your free consultation!