Angular State Management
State management organizes how data changes over time.
State Management Essentials
- Local first: Start with component signals; promote to a service (store) only when sharing is needed.
- Signals: Use
signal()for local and service-backed state; derive withcomputed(). - Interop: Use RxJS only when stream semantics are required; keep global state minimal.
import { Injectable, signal, computed, inject } from '@angular/core';@Injectable({ providedIn: 'root' })class CounterStore { count = signal(0); double = computed(() => this.count() * 2); inc() { this.count.update(n => n + 1); }}// Component: const store = inject(CounterStore);Notes:
- Related: SeeSignals,Services, andChange Detection.
- Start with a simple service + signals; reach for libraries only when complexity demands it.
Service-backed Signals (Store)
Lift state into a service to share across components.
It improves reuse.
import { Injectable, signal, inject } from '@angular/core';@Injectable({ providedIn: 'root' })class CounterStore { count = signal(0); inc() { this.count.update(n => n + 1); }}// Component usageclass App { store = inject(CounterStore); }Example
import { bootstrapApplication } from '@angular/platform-browser';import { Component, Injectable, signal, inject } from '@angular/core';@Injectable({ providedIn: 'root' })class CounterStore { count = signal(0); inc() { this.count.update(n => n + 1); }}@Component({ selector: 'app-root', standalone: true, template: ` <h3>Service with Signals</h3> <p>Count: {{ store.count() }}</p> <button (click)="store.inc()">Increment</button> `})class App { store = inject(CounterStore); }bootstrapApplication(App);<app-root></app-root>Example explained
CounterStore: A service holding asignaland an update method (inc()).inject(CounterStore): Retrieves the store in the component (no constructor required).store.count(): Read the current value in the template; clicking the button callsstore.inc().
Design tips:
- Keep a single source of truth per feature in a service; inject where needed.
- Expose methods for updates; avoid mutating state from components directly.
- Derive values with computed signals; keep side effects (like persistence) in the service.
- Bridge to streams only when needed (e.g., to interop with RxJS-based APIs).
Local vs Global State
- Keep most state local to components to reduce coupling.
- Promote to a shared service only when multiple features need it.
- Scope providers at the feature/route level when isolation is desired.
import { provideRouter, Routes } from '@angular/router';const routes: Routes = [ { path: '', component: Home, providers: [CounterStore] }];bootstrapApplication(App, { providers: [provideRouter(routes)] });Guidelines:
- Promote state when multiple routes/components need the same data or when caching improves UX.
- Separate UI state (filters, dialogs) from server/cache state; manage them independently.
- Initialize lazily on first use and consider reset points (e.g., on logout).

