ReviseAlgo Logo

State Management

Signal Store

Master NgRx Signal Store, covering functional state slices, custom methods, computed selectors, and signals-first state management.

Last Updated: July 15, 2026 12 min read

1. Learning Objectives

In this lesson, you will master the NgRx Signal Store, a lightweight, functional, signals-first state management library. By the end of this topic, you will be able to:

  • Explain the architecture of NgRx Signal Store and how it differs from traditional Redux Stores.
  • Initialize state properties dynamically using the withState builder plugin.
  • Define computed derived state selectors utilizing withSelectors.
  • Write state mutation methods and side-effects inside withMethods.
  • Coordinate lifecycle events in stores using withHooks.

2. Overview

The NgRx Signal Store is a functional state management library for Angular built natively on top of the Signals API. It allows developers to define state, selectors, and methods in a modular, tree-shakeable structure. By combining state properties as signals, it eliminates the actions-reducers-effects boilerplate of traditional Redux, offering a clean API surface that can be injected as a local component provider or a global singleton.

3. Why This Topic Matters

Signal Store solves the scalability and boilerplate issues of traditional store frameworks:

  • Boilerplate Reduction: Traditional Redux requires writing separate actions, reducers, selectors, and effects files for simple state slices. Signal Store consolidates these definitions into a single, functional builder pattern.
  • Signals Native: As Angular transitions to signals-based fine-grained change detection (Zoneless), relying on RxJS observables in templates introduces mapping overhead. Signal Store exposes state slices directly as read-only signals.

4. Real-World Analogy

Think of NgRx Signal Store like **a Smart Vending Machine**:

  • The Inventory State (withState): The items inside the machine (e.g. sodas, chips, chocolate) and their quantities are stored in database arrays.
  • The Display Screen (withSelectors): Displays filtered calculations, like showing which products are "Sold Out" or showing the current balance you have inserted. The screen updates instantly as state changes.
  • The Control Buttons (withMethods): You press buttons (e.g. "Select Soda 12", "Insert Coin") to purchase an item. The machine processes your input, updates the inventory list, and returns your item without needing separate bank vaults or ledgers.
  • Machine Diagnostics (withHooks): Self-checks its sensors and connections on startup (OnInit) and safely shuts down its mechanical arms when turned off (OnDestroy).

5. Core Concepts

Below is a comparison of traditional NgRx Global Store versus the NgRx Signal Store:

Property NgRx Global Store (Classic Redux) NgRx Signal Store (Modern Signals)
Reactivity Base RxJS Observables (Requires async pipes / subscribe). Angular Signals (Synchronous, getter-based values).
Boilerplate Level High (Requires Actions, Reducers, Effects files). Low (Consolidated functional builder pattern).
Granularity Scope Global singleton. Highly flexible (Global singleton or local component state).
Tree-shaking Support Difficult due to centralized static registration. Excellent (Functional plugins compile on demand).

6. Syntax & API Reference

Below is the basic syntax for constructing a state store using the signalStore builder:

7. Visual Diagram

This diagram displays how state, selectors, and methods coordinate inside a Signal Store:

8. Live Example — Full Working Code

Here is a complete standalone task manager dashboard using a local injected TodoStore that filters entries reactively:

What just happened? The TodoDashboardComponent registers the TodoStore under its providers array, ensuring it gets a dedicated local state scope. Inside the template, we query the store properties as read-only signals (e.g. store.filteredTodos()). Clicking a list item calls store.toggleTodo(), which calls patchState() to update the model. Angular detects the signal change and re-renders the item.

9. Interactive Playground

Try It Yourself Challenges:

  1. Add a withHooks block that logs "TodoStore Initialized" inside the onInit hook and "TodoStore Cleared" inside onDestroy.
  2. Register the TodoStore using @Injectable({ providedIn: 'root' }) instead of component providers, and verify that mounting multiple dashboard widgets causes them to share the exact same list state.

10. Common Mistakes

Mistake Why it happens Wrong Correct
Mutating State Properties directly Attempting to assign values directly to store properties outside patchState. Store signals are read-only. store.filter = 'all' patchState(store, { filter })
Omitting Spread Operators in patchState Mutating child properties of a state object reference directly. patchState does a shallow merge only. patchState(store, (state) => {
state.list.push(item);
return state;
})
patchState(store, (state) => ({
list: [...state.list, item]
}))

11. Best Practices

  • Restrict Store writes: Always perform state updates exclusively inside methods exposed by withMethods using patchState().
  • Keep selectors optimized: Ensure computed selectors inside withSelectors are pure functions that avoid complex side effects.
  • Use local providers for transient state: Register stores in component providers arrays for widgets that should reset their state when unmounted.
  • Utilize RxJS when necessary: Use the rxMethod helper from `@ngrx/signals/rxjs-interop` to handle asynchronous events inside methods.

12. Browser Compatibility/Requirements

NgRx Signal Store requires Angular 16+ or newer and performs consistently across all modern web browsers.

13. Interview Questions

Q1: How does NgRx Signal Store manage state updates, and what role does patchState play?

Answer: Signal Store state is immutable. State updates must go through the patchState() function. It takes the store instance and either an object representing the updated state slice or a updater function returning the next state reference. patchState() performs a shallow merge and notifies all dependent signal consumers synchronously.

Q2: Can NgRx Signal Store be used as both a global singleton and a local component provider?

Answer: Yes. Unlike traditional global stores, Signal Store is highly flexible. To make it a global singleton, decorate it with @Injectable({ providedIn: 'root' }). To isolate state to a specific component lifecycle (so it destroys when the component unmounts), register it in the component's providers array instead.

14. Debugging Exercise

Identify why the compiler throws an error stating that property "count" is read-only when executing the increment method:

View Solution

Diagnosis: Properties exposed by signalStore are read-only signals. They do not expose writable methods like .set() or .update() directly. To update state inside a method, you must call patchState().

Fix: Wrap updates in patchState().

15. Practice Exercises

Exercise 1: Create a theme configuration store

Build a global ThemeStore using Signal Store. Include state parameters for theme mode (light/dark) and layout sidebar collapse toggles. Expose methods to update states, and verify all routing views read state values reactively.

16. Scenario-Based Challenge

The Dynamic Stock Trades Watcher:

You are designing a high-frequency trading desk view. Stocks arrive via custom sockets and are appended to state list arrays. State calculations must compile average pricing dynamically across differing stock categories. Design a local component-level Signal Store with custom selectors computing average prices lazily, caching calculations to keep view rendering performant under rapid updates.

17. Quick Quiz

Q1: Which helper function from @ngrx/signals must be called to modify store states?

A) updateState()

B) patchState()

C) storeState()

Answer: B — patchState() is the only API utility that performs safe state slice updates.

18. Summary & Key Takeaways

  • Signal Store is a functional, signals-first state management library for Angular.
  • It exposes state slices as read-only signals, eliminating classic Redux action/reducer boilerplates.
  • Updates are executed inside custom methods using the patchState() helper.
  • Stores are highly flexible and can be injected as global singletons or local component providers.

19. Cheat Sheet

Builder Plugin Purpose
withState(initialState) Defines the baseline state model properties.
withSelectors(store => computed) Defines computed derived projections.
withMethods(store => methods) Defines state mutation operations.
withHooks({ onInit, onDestroy }) Hooks into store initialization and destruction lifecycles.
---