Angular Signals Deep Dive 2026: Reactive State Management Without RxJS
Angular Signals Deep Dive 2026: Reactive State Management Without RxJS
import { signal, computed } from '@angular/core';
export class CartService {
items = signal<CartItem[]>([]);
totalPrice = computed(() =>
this.items().reduce((sum, item) => sum + item.price * item.qty, 0)
);
totalItems = computed(() =>
this.items().reduce((sum, item) => sum + item.qty, 0)
);
addItem(item: CartItem) {
this.items.update(current => [...current, item]);
}
removeItem(id: string) {
this.items.update(current => current.filter(i => i.id !== id));
}
}@Component({
selector: 'app-counter',
template: `<button (click)="increment()">Count: {{ count() }}</button>`
})
export class CounterComponent {
count = input.required<number>(); // signal input
increment = output<number>(); // signal output
}Angular Signals have transformed how we handle reactive state in Angular applications. If you're still relying heavily on RxJS for simple state management, it's time to explore Signals as a cleaner alternative.
What Are Angular Signals?
Signals are a reactive primitive introduced in Angular 16 and now fully matured in Angular 19. They provide a synchronous, glitch-free way to track and propagate state changes throughout your application. Unlike Observables, Signals always have a current value and don't require subscriptions.
Core Signal Types
1. signal() - Creates a writable signal with an initial value
2. computed() - Derives a read-only signal from other signals
3. effect() - Runs side effects when signal values change
Practical Example: Shopping Cart
import { signal, computed } from '@angular/core';
export class CartService {
items = signal<CartItem[]>([]);
totalPrice = computed(() =>
this.items().reduce((sum, item) => sum + item.price * item.qty, 0)
);
totalItems = computed(() =>
this.items().reduce((sum, item) => sum + item.qty, 0)
);
addItem(item: CartItem) {
this.items.update(current => [...current, item]);
}
removeItem(id: string) {
this.items.update(current => current.filter(i => i.id !== id));
}
}@Component({
selector: 'app-counter',
template: `<button (click)="increment()">Count: {{ count() }}</button>`
})
export class CounterComponent {
count = input.required<number>(); // signal input
increment = output<number>(); // signal output
}
Excellent overview of Angular Signals! The shopping cart example is a practical way to demonstrate how signals work in real scenarios. I agree that keeping RxJS for complex async operations while using Signals for simple state management is the right approach. The signal inputs and outputs in components are a game changer for cleaner component APIs.