App state with NgRx SignalStore
signal() buried at the bottom of a component. As soon as a piece of state is shared, derived and mutated from several places, you want a clear boundary: read-only selectors, methods to evolve it. NgRx SignalStore ( @ngrx/signals ) gives you exactly that, without the action/reducer boilerplate of classic NgRx.
Anatomy of a signalStore
withState declares the initial state, withComputed the derived values, withMethods the operations. Each state field automatically becomes a signal exposed on the instance.
patchState , which applies an immutable update and notifies the affected signals.
Selectors that are signals
store.total and store.count aren't functions you call inside the service: they are computed s, so full-fledged signals. In a component you read them like any other signal, and zoneless change detection only re-renders what depends on them.
Composing with async calls
withMethods can integrate rxMethod (from @ngrx/signals/rxjs-interop ) to wire an RxJS stream, or simply async / await for a fetch . You keep orchestration logic inside the store, and the component stays a view. That's also where you place a loading state for a stale-while-revalidate pattern.
Store or plain signal?
signal() : a store would add pointless indirection. The SignalStore earns its place when state is:
- ›
shared across several components or routes; - ›
derived by several computeds you want to centralize; - ›
mutated by operations you want to test in isolation.
The SignalStore isn't yesterday's "actions everywhere" NgRx. It's a façade of signals: read-only out, methods in, zero reducers. You keep the discipline of a store without paying its ceremony.