Skip to main content
NgRx State Management

NgRx Overview

Estimated Time: 4 hours | Difficulty: Advanced | Prerequisites: RxJS, Services, Signals
NgRx is a reactive state management library for Angular applications, inspired by Redux. Think of it like a centralized ledger for your entire application — every piece of state lives in one place, every change is recorded as an explicit event, and any part of the UI can subscribe to exactly the slice of data it needs. This makes complex data flows predictable and testable, much like how a bank’s transaction log makes every account balance auditable. When do you actually need NgRx? Not every app does. If your state is mostly local to components or shared between a parent and a few children, simple services with signals will do. NgRx earns its complexity when you have state shared across many unrelated components, need undo/redo or time-travel debugging, or when multiple data sources interact in ways that are hard to reason about without a single source of truth.

Setting Up NgRx

Configure Store


Actions

Actions describe unique events in your application. Think of them as newspaper headlines — they announce what happened (not what should happen next). Good action names read like past-tense facts: “Products Loaded Successfully,” “User Clicked Delete Button.” This naming discipline is critical because it decouples the event from the reaction, letting you change how you respond to events without rewriting the event itself.
Common pitfall: Naming actions as commands (“Load Products”) rather than events (“Load Products Requested”). Command-style names couple the action to a specific handler. Event-style names let multiple reducers and effects respond to the same action independently.

Reducers

Reducers are pure functions that handle state transitions. They take the current state and an action, and return a new state object — never mutating the original. Think of a reducer like a bank teller: they receive a transaction slip (action), look at the current balance (state), and write a new balance (next state). The old balance is never erased — it is simply superseded. This immutability is what makes time-travel debugging and change detection possible.

Selectors

Selectors are pure functions for deriving and composing state. They serve as the “query layer” for your store — like SQL views over a database. Selectors are memoized by default, meaning they only recompute when their input state actually changes. This is crucial for performance: if you have 10 components reading selectFilteredProducts, the filtering logic runs once per state change, not 10 times.
Practical tip: Build selectors bottom-up. Start with simple “leaf” selectors that read a single property, then compose them into richer “view model” selectors. The selectProductsViewModel pattern at the bottom of this section is the gold standard — it gives your component exactly one observable to subscribe to, with all the derived data pre-computed.

Effects

Effects handle side effects like API calls. If reducers are the “pure” part of NgRx (given the same input, always the same output), effects are where the messy real world lives — network requests, localStorage, timers, navigation. Think of effects as backstage crew in a theater: the audience (components) sees the polished result, but effects are behind the curtain doing the actual work of fetching data and dispatching success/failure actions. The most important decision in an effect is which RxJS flattening operator to use. The wrong operator choice is the single most common source of NgRx bugs. See the decision guide at the bottom of this section.

Using NgRx in Components

Using Signal Store (NgRx 17+)


NgRx Entity

Entity adapter for managing collections efficiently. Instead of storing entities in a plain array (where finding an item by ID is O(n)), NgRx Entity stores them in a normalized shape: an ids array for ordering and an entities dictionary for O(1) lookups. Think of it like a database table with a primary key index — you get fast reads, and the adapter handles all the bookkeeping for add, update, upsert, and remove operations.
When to use Entity: Any time you have a collection of items with unique IDs that you need to frequently look up, update, or filter. Product catalogs, user lists, todo items, chat messages — these are all perfect Entity candidates.

Best Practices

Feature Stores

Organize state by feature with lazy-loaded reducers

Facade Pattern

Create facades to simplify store interactions

Selector Composition

Build complex selectors from simple ones

Effect Operators

Choose the right flattening operator for each use case

Practice Exercise

1

Build Shopping Cart

Implement a complete cart with NgRx including persistence
2

Add Undo/Redo

Implement undo/redo functionality using NgRx
3

Migrate to Signal Store

Convert a traditional NgRx store to Signal Store

Next: Angular Animations

Create fluid animations and transitions with Angular’s animation system