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.
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 →