NgRx Store

Redux for Angular — When You Outgrow Services

NgRx Store

NgRx is the canonical Redux-style state container for Angular. Reach for it when state crosses many features and side effects pile up.

5 min read Level 3/5 #angular#state#ngrx
What you'll learn
  • Install @ngrx/store and define actions, reducers, selectors
  • Dispatch actions and select state from a component
  • Know about NgRx Effects and SignalStore

NgRx is Redux for Angular: a single immutable store, actions that describe events, reducers that produce the next state, and selectors that derive views. Use it when state is shared across many features and side effects deserve their own layer.

Install and provide

npm install @ngrx/store @ngrx/effects
// app.config.ts
import { provideStore } from '@ngrx/store';
import { provideEffects } from '@ngrx/effects';
import { usersReducer } from './users/users.reducer';
import { UsersEffects } from './users/users.effects';

export const appConfig = {
  providers: [
    provideStore({ users: usersReducer }),
    provideEffects([UsersEffects]),
  ],
};

Actions and reducer

import { createAction, createReducer, on, props } from '@ngrx/store';

export interface User { id: string; name: string }

export const loadUsers = createAction('[Users] Load');
export const loadUsersSuccess = createAction(
  '[Users] Load Success',
  props<{ users: User[] }>(),
);

interface State { users: User[]; loading: boolean }
const initial: State = { users: [], loading: false };

export const usersReducer = createReducer(
  initial,
  on(loadUsers, s => ({ ...s, loading: true })),
  on(loadUsersSuccess, (s, { users }) => ({ ...s, users, loading: false })),
);

Selectors

import { createFeatureSelector, createSelector } from '@ngrx/store';

const selectUsersState = createFeatureSelector<State>('users');
export const selectUsers = createSelector(selectUsersState, s => s.users);

Dispatch and select

import { Component, inject } from '@angular/core';
import { Store } from '@ngrx/store';
import { toSignal } from '@angular/core/rxjs-interop';
import { loadUsers, selectUsers } from './users';

@Component({ selector: 'app-users', standalone: true, template: '...' })
export class UsersComponent {
  private store = inject(Store);
  users = toSignal(this.store.select(selectUsers), { initialValue: [] });

  constructor() {
    this.store.dispatch(loadUsers());
  }
}

When NgRx earns its weight

For most apps, a service that exposes signals is plenty. NgRx pays off when you have time-travel debugging needs, complex async flows, or many features touching shared state. The newer NgRx SignalStore offers the same patterns with a lighter, signal-first API.

State Management Patterns →