State Management
Share state across unrelated components using a signal-based service, and understand when a dedicated library like NgRx is worth it.
The Simplest Approach: A Shared Service
Because a providedIn: 'root' service is a singleton shared across the whole app, a plain service holding state (backed by signals) is often all a project genuinely needs — no dedicated state library required.
A Signal-Based Store Service
import { Injectable, signal, computed } from '@angular/core';
interface CartItem { id: number; name: string; price: number;}
@Injectable({ providedIn: 'root' })export class CartStore { private items = signal<CartItem[]>([]);
readonly cartItems = this.items.asReadonly(); readonly totalPrice = computed(() => this.items().reduce((sum, item) => sum + item.price, 0) );
addItem(item: CartItem) { this.items.update(current => [...current, item]); }
removeItem(id: number) { this.items.update(current => current.filter(item => item.id !== id)); }}// Any component can inject and use itexport class CartSummaryComponent { private cartStore = inject(CartStore); items = this.cartStore.cartItems; total = this.cartStore.totalPrice;}Exposing cartItems as a read-only signal (rather than the raw, writable items signal) means components can read the cart, but must go through addItem/removeItem to change it — keeping mutations centralized and traceable.
When to Reach for NgRx
NgRx is Angular's most established dedicated state management library, built around the Redux pattern — a single store, dispatched actions, and pure reducer functions. It adds real structure (and real boilerplate) that pays off specifically at a certain scale.
A Shared Signal Service Is Enough When...
- State is scoped to one feature or a handful of components
- The update logic is straightforward
- You want to avoid extra dependencies and boilerplate
Consider NgRx When...
- State changes need to be traceable/time-travel debuggable across a large app
- Many independent teams touch the same shared state
- You need strict, enforced unidirectional data flow at scale
FAQs
No — many production Angular apps use nothing more than well-organized, signal-based services, reserving NgRx for genuinely large, complex state graphs.
Yes — NgRx has increasingly embraced signals (via NgRx SignalStore), narrowing the gap between a lightweight service-based approach and a full dedicated store.
Summary
A signal-based service covers the vast majority of Angular state-sharing needs, with NgRx available for the specific cases where its added structure genuinely pays off. With forms, modules, and state covered, the next section moves into advanced customization — starting with your own directives and pipes.