NgRx Signal Store vs klassieke NgRx in 2026: welke oplossing past bij jouw Angular project?
Vergelijking van NgRx Signal Store en klassieke NgRx voor Angular state management. NgRx 22 features, code voorbeelden en aanbevelingen.

NgRx Signal Store vertegenwoordigt een fundamentele verschuiving in Angular state management. Het vervangt het traditionele Redux-geïnspireerde patroon van actions, reducers en selectors door een signal-gebaseerde reactieve aanpak. Met NgRx 22 (uitgebracht augustus 2026) krijgt Signal Store resource extensions, dynamische deep signals en verbeterde composability. Deze vergelijking onderzoekt beide oplossingen om Angular-ontwikkelaars te helpen weloverwogen architecturale beslissingen te nemen.
Gebruik Signal Store voor feature-level state, middelmatig complexe applicaties en integratie met Angular's native Resource API. Gebruik klassieke NgRx voor enterprise applicaties die strikte audit trails, complexe side effects en uitgebreide DevTools debugging vereisen.
De architecturale verschillen begrijpen
Klassieke NgRx volgt het Redux patroon: actions beschrijven gebeurtenissen, reducers produceren nieuwe immutable state, en selectors extraheren data. Elke state-wijziging doorloopt een voorspelbare, traceerbare pipeline.
Signal Store hanteert een andere aanpak. In plaats van actions door reducers te dispatchen, leeft de state in reactieve signals. Updates gebeuren via methodes die patchState() aanroepen, en afgeleide waarden komen van computed signals in plaats van gememoriseerde selectors.
| Aspect | Klassieke NgRx | Signal Store |
|---|---|---|
| Mentaal model | Redux/Flux | Reactieve services |
| Boilerplate | Hoog (actions, reducers, selectors, effects) | Laag (state, methodes, computed) |
| Reactiviteit | RxJS Observables | Angular Signals |
| Resource API | Handmatige integratie | Natief met @ngrx/signals/resource |
| DevTools | Volledige Redux DevTools | Signal Store DevTools (via plugin) |
| Leercurve | Steil | Gematigd |
| Bundle grootte | Groter (~15-20KB gzipped) | Kleiner (~4KB gzipped) |
Voor ontwikkelaars die al bekend zijn met Angular Signals, voelt Signal Store als een natuurlijke uitbreiding van de reactieve framework primitieven.
Klassieke NgRx: het Redux patroon in Angular
Klassieke NgRx organiseert state management rond actions, reducers, selectors en effects. Deze scheiding dwingt voorspelbaarheid af ten koste van uitgebreidheid.
import { createActionGroup, emptyProps, props } from '@ngrx/store';
import { Book } from './book.model';
export const BooksActions = createActionGroup({
source: 'Books',
events: {
'Load Books': emptyProps(),
'Load Books Success': props<{ books: Book[] }>(),
'Load Books Failure': props<{ error: string }>(),
'Add Book': props<{ book: Book }>(),
'Remove Book': props<{ id: string }>(),
},
});De createActionGroup API groepeert gerelateerde actions onder één bron, wat de leesbaarheid in DevTools verbetert en boilerplate vermindert in vergelijking met individuele createAction aanroepen.
import { createReducer, on } from '@ngrx/store';
import { BooksActions } from './books.actions';
import { Book } from './book.model';
export interface BooksState {
books: Book[];
loading: boolean;
error: string | null;
}
const initialState: BooksState = {
books: [],
loading: false,
error: null,
};
export const booksReducer = createReducer(
initialState,
on(BooksActions.loadBooks, (state) => ({
...state,
loading: true,
error: null,
})),
on(BooksActions.loadBooksSuccess, (state, { books }) => ({
...state,
books,
loading: false,
})),
on(BooksActions.loadBooksFailure, (state, { error }) => ({
...state,
loading: false,
error,
}))
);Reducers blijven pure functies die nieuwe state objecten retourneren. De on() functie handelt action matching en state updates beknopt af.
import { createFeatureSelector, createSelector } from '@ngrx/store';
import { BooksState } from './books.reducer';
export const selectBooksState = createFeatureSelector<BooksState>('books');
export const selectAllBooks = createSelector(
selectBooksState,
(state) => state.books
);
export const selectBooksLoading = createSelector(
selectBooksState,
(state) => state.loading
);
export const selectPublishedBooks = createSelector(
selectAllBooks,
(books) => books.filter((book) => book.published)
);Selectors bieden automatisch memoization. De selectPublishedBooks selector herberekent alleen wanneer de books array verandert, waardoor reads efficiënt zijn zelfs in vaak bijgewerkte stores.
NgRx Signal Store: signals-first state management
Signal Store consolideert state, computed waarden en methodes in één injecteerbare service. De officiële NgRx documentatie beschrijft het als een "volledig uitgeruste state management oplossing met native ondersteuning voor Angular Signals".
import { computed, inject } from '@angular/core';
import { patchState, signalStore, withComputed, withMethods, withState } from '@ngrx/signals';
import { BooksService } from './books.service';
import { Book } from './book.model';
interface BooksState {
books: Book[];
loading: boolean;
error: string | null;
}
const initialState: BooksState = {
books: [],
loading: false,
error: null,
};
export const BooksStore = signalStore(
{ providedIn: 'root' },
withState(initialState),
withComputed(({ books }) => ({
publishedBooks: computed(() => books().filter((book) => book.published)),
totalBooks: computed(() => books().length),
})),
withMethods((store, booksService = inject(BooksService)) => ({
async loadBooks() {
patchState(store, { loading: true, error: null });
try {
const books = await booksService.getAll();
patchState(store, { books, loading: false });
} catch (error) {
patchState(store, { loading: false, error: 'Failed to load books' });
}
},
addBook(book: Book) {
patchState(store, ({ books }) => ({ books: [...books, book] }));
},
removeBook(id: string) {
patchState(store, ({ books }) => ({
books: books.filter((book) => book.id !== id),
}));
},
}))
);Het volledige state management van een feature past in één bestand. State properties worden automatisch signals, computed signals vervangen selectors, en methodes vervangen action dispatches.
NgRx 22: Resource Extensions en withProps
NgRx 22 introduceert het @ngrx/signals/resource entry point, waarmee directe integratie met Angular's Resource API mogelijk wordt. De withProps feature centraliseert service injection, terwijl resource extensions gangbare async patronen afhandelen.
import { computed, inject } from '@angular/core';
import { patchState, signalStore, withComputed, withMethods, withProps } from '@ngrx/signals';
import { extendResource, withPreviousValueOnLoading, withValueOnError } from '@ngrx/signals/resource';
import { httpResource } from '@angular/core';
import { BooksService } from './books.service';
import { Book } from './book.model';
export const BooksResourceStore = signalStore(
{ providedIn: 'root' },
// Services injecteren via withProps (privé per conventie met underscore)
withProps(() => ({
_booksService: inject(BooksService),
})),
// Resources opzetten met extensions
withProps((store) => ({
_booksResource: extendResource(
httpResource<Book[]>({
url: '/api/books',
defaultValue: [],
}),
withPreviousValueOnLoading(), // Houdt laatste waarde tijdens herladen
withValueOnError([]) // Retourneert lege array bij fout
),
})),
// Readonly resource beschikbaar stellen aan consumers
withProps((store) => ({
booksResource: store._booksResource.asReadonly(),
})),
withComputed((store) => ({
books: computed(() => store.booksResource.value()),
isLoading: computed(() => store.booksResource.isLoading()),
publishedBooks: computed(() =>
store.booksResource.value().filter((book) => book.published)
),
}))
);Resource extensions lossen twee veelvoorkomende problemen op: flikkeren tijdens herladen (opgelost door withPreviousValueOnLoading) en foutafhandeling (opgelost door withValueOnError of withPreviousValueOnError). Deze extensions zijn composeerbaar en kunnen globaal worden aangeboden via provideResourceExtensions.
Entity management met withEntities
Beide benaderingen ondersteunen entity collecties, maar de withEntities feature van Signal Store reduceert de code drastisch.
import { computed, inject } from '@angular/core';
import { patchState, signalStore, withComputed, withMethods } from '@ngrx/signals';
import {
addEntity,
removeEntity,
setAllEntities,
updateEntity,
withEntities,
} from '@ngrx/signals/entities';
import { BooksService } from './books.service';
import { Book } from './book.model';
export const BooksEntityStore = signalStore(
{ providedIn: 'root' },
withEntities<Book>(),
withComputed(({ entities }) => ({
publishedBooks: computed(() => entities().filter((book) => book.published)),
})),
withMethods((store, booksService = inject(BooksService)) => ({
async loadBooks() {
const books = await booksService.getAll();
patchState(store, setAllEntities(books));
},
addBook(book: Book) {
patchState(store, addEntity(book));
},
updateBook(id: string, changes: Partial<Book>) {
patchState(store, updateEntity({ id, changes }));
},
removeBook(id: string) {
patchState(store, removeEntity(id));
},
}))
);De withEntities feature biedt automatisch ids, entityMap en entities signals. Entity updaters zoals addEntity, removeEntity en updateEntity handelen collectie mutaties af.
Klaar om je Angular gesprekken te halen?
Oefen met onze interactieve simulatoren, flashcards en technische tests.
Custom Store Features voor herbruikbaarheid
De signalStoreFeature functie van Signal Store maakt het mogelijk om cross-cutting concerns te extraheren en te hergebruiken over stores. NgRx 22 voegt SignalStoreFeatureType toe om type-extractie te vereenvoudigen.
import { computed } from '@angular/core';
import { signalStoreFeature, withComputed, withState, SignalStoreFeatureType } from '@ngrx/signals';
type RequestStatus = 'idle' | 'pending' | 'fulfilled' | 'error';
export function withRequestStatus() {
return signalStoreFeature(
withState<{ requestStatus: RequestStatus }>({ requestStatus: 'idle' }),
withComputed(({ requestStatus }) => ({
isPending: computed(() => requestStatus() === 'pending'),
isFulfilled: computed(() => requestStatus() === 'fulfilled'),
isError: computed(() => requestStatus() === 'error'),
}))
);
}
// NgRx 22: types extraheren zonder handmatige herdeclaratie
export type RequestStatusFeature = SignalStoreFeatureType<typeof withRequestStatus>;
export function setPending(): { requestStatus: RequestStatus } {
return { requestStatus: 'pending' };
}
export function setFulfilled(): { requestStatus: RequestStatus } {
return { requestStatus: 'fulfilled' };
}
export function setError(): { requestStatus: RequestStatus } {
return { requestStatus: 'error' };
}Deze custom feature kan nu gecomposeerd worden in elke store die loading state tracking nodig heeft.
import { inject } from '@angular/core';
import { patchState, signalStore, withMethods } from '@ngrx/signals';
import { setAllEntities, withEntities } from '@ngrx/signals/entities';
import { withRequestStatus, setFulfilled, setPending, setError } from './with-request-status';
import { BooksService } from './books.service';
import { Book } from './book.model';
export const BooksStore = signalStore(
{ providedIn: 'root' },
withEntities<Book>(),
withRequestStatus(),
withMethods((store, booksService = inject(BooksService)) => ({
async loadBooks() {
patchState(store, setPending());
try {
const books = await booksService.getAll();
patchState(store, setAllEntities(books), setFulfilled());
} catch {
patchState(store, setError());
}
},
}))
);De gecomposeerde store stelt nu isPending(), isFulfilled() en isError() computed signals beschikbaar zonder logica te dupliceren.
Synchrone state tracking met watchState
NgRx 22 voegt de watchState functie toe voor synchrone state change tracking, nuttig voor undo/redo functionaliteit of state geschiedenis.
import { inject, DestroyRef } from '@angular/core';
import { watchState } from '@ngrx/signals';
import { BooksStore } from './books.store';
export function setupStateHistory() {
const store = inject(BooksStore);
const destroyRef = inject(DestroyRef);
const history: unknown[] = [];
// Synchrone tracking: vangt elke state wijziging, geen coalescing
watchState(store, (state) => {
history.push(structuredClone(state));
});
return { history };
}In tegenstelling tot effect-gebaseerde tracking die updates samenvoegt, vuurt watchState synchroon bij elke patchState aanroep, waardoor de volledige state geschiedenis wordt vastgelegd.
Component integratie patronen
Signal Store integreert natuurlijk met Angulars template syntax door signal reads.
import { Component, inject } from '@angular/core';
import { BooksStore } from './books.store';
@Component({
selector: 'app-books-list',
standalone: true,
template: `
@if (store.isPending()) {
<div class="loading-spinner">Loading books...</div>
}
@if (store.isError()) {
<div class="error-message">Failed to load books</div>
}
@for (book of store.entities(); track book.id) {
<div class="book-card">
<h3>{{ book.title }}</h3>
<p>{{ book.author }}</p>
<button (click)="store.removeBook(book.id)">Remove</button>
</div>
}
<p>Total: {{ store.totalBooks() }} books</p>
`,
})
export class BooksListComponent {
readonly store = inject(BooksStore);
constructor() {
this.store.loadBooks();
}
}Geen subscription management nodig. Geen async pipes. Signals integreren met Angulars zoneless change detection voor optimale performance. Met Angular 22's OnPush als standaard change detection strategie profiteren Signal Store componenten van precieze, granulaire updates.
Performance en bundle grootte overwegingen
De lichtere architectuur van Signal Store resulteert in kleinere bundles. Het core @ngrx/signals package voegt ongeveer 4KB gzipped toe, vergeleken met 15-20KB voor de volledige klassieke NgRx stack (@ngrx/store, @ngrx/effects, @ngrx/entity).
Signal Store profiteert ook van Angulars signal-gebaseerde change detection. Computed signals herberekenen alleen wanneer dependencies veranderen, en componenten re-renderen alleen wanneer geconsumeerde signals updaten. Deze granulaire reactiviteit elimineert onnodige change detection cycli die gebruikelijk zijn in Observable-gebaseerde patronen.
Voor applicaties die RxJS al intensief gebruiken, integreren de Observable-gebaseerde selectors van klassieke NgRx naadloos met bestaande pipelines.
NgRx 22 vereist Angular 22.x, TypeScript 6.0.x en RxJS 6.5.x of 7.5.x. Update met ng update @ngrx/signals@22 voor Signal Store projecten.
Migratiestrategie van klassiek naar Signal Store
Het migreren van een bestaande klassieke NgRx applicatie vereist geen big-bang herschrijving. Beide oplossingen kunnen naast elkaar bestaan, wat incrementele adoptie mogelijk maakt.
- Begin met nieuwe features met Signal Store
- Migreer geïsoleerde feature modules één voor één
- Behoud complexe, effect-intensieve features op klassieke NgRx als de migratiekosten de voordelen overtreffen
- Gebruik de NgRx Signals interop utilities om te bridgen tussen Observable-gebaseerde en signal-gebaseerde state
Klassieke NgRx Store en Signal Store kunnen naast elkaar draaien in dezelfde applicatie. Het NgRx team ondersteunt officieel beide benaderingen en biedt interop utilities om ze te verbinden.
Wanneer elke aanpak kiezen
Kies Signal Store wanneer:
- Nieuwe Angular 22+ applicaties gebouwd worden
- Feature-level of component-level state beheerd wordt
- Integratie met Angulars Resource API en Signal Forms plaatsvindt
- Developer experience en gereduceerde boilerplate prioriteit hebben
- Het team beperkte NgRx ervaring heeft
- Bundle grootte belangrijk is
Kies klassieke NgRx wanneer:
- Bestaande NgRx applicaties onderhouden worden
- Strikte action logging en audit trails vereist zijn
- Complexe workflows met uitgebreide side effects gebouwd worden
- Volledige Redux DevTools time-travel debugging nodig is
- Het team diepgaande NgRx expertise bezit
Voor state management fundamenten bereidt het begrijpen van beide benaderingen ontwikkelaars voor op diverse Angular codebases.
Bronnen
- NgRx v22 Release Announcement - Resource extensions, dynamic deep signals, SignalStoreFeatureType
- Angular 22 Feature Overview - Signal Forms stable, OnPush default, Resource API
- Using Angular's Resource API with NgRx Signal Store - withProps patterns, httpResource integratie
Begin met oefenen!
Test je kennis met onze gespreksimulatoren en technische tests.
Signal Store vs klassieke NgRx: belangrijkste punten
- Signal Store reduceert state management boilerplate met 60-70% vergeleken met klassieke NgRx
- NgRx 22 voegt resource extensions toe (
withPreviousValueOnLoading,withValueOnError) voor naadloze async state afhandeling - De
withPropsfeature centraliseert service injection, waardoor herhaalde parameterlijsten over features worden geëlimineerd watchStatemaakt synchrone state tracking mogelijk voor undo/redo en state geschiedenis- Klassieke NgRx blijft de betere keuze voor enterprise applicaties die strikte audit trails en complexe side effects vereisen
- Beide benaderingen kunnen naast elkaar bestaan in dezelfde applicatie, wat incrementele migratie mogelijk maakt
- Voor nieuwe Angular 22+ projecten zou Signal Store de standaardkeuze moeten zijn tenzij specifieke requirements klassieke NgRx vereisen
Zie jij de bug in Angular?
Een echt codefragment, een verborgen bug, één poging per dag. Zonder account uit te proberen.

Geschreven door
Anthony Fillion-MailletOprichter van SharpSkill
Al meer dan 10 jaar fullstack-ontwikkelaar. Hij leidt SharpSkill en staat in voor alles wat hier verschijnt.
Bijgewerkt op 9 oktober 2026
Delen
Gerelateerde artikelen

Freelance Angular developer vacatures 2026: opdrachten vinden, dagtarieven en voorbereiding
Freelance Angular developers profiteren in 2026 van stabiele dagtarieven en enterprise-gedreven projecten. Overzicht van opdrachtsplatforms, vergoedingen en sollicitatievoorbereidingen.

Angular developer vacatures in 2026: waar zoeken, salarissen en sollicitatievoorbereidng
De arbeidsmarkt voor Angular developers in september 2026: actieve vacatures, salarisstatistieken, remote mogelijkheden en gerichte sollicitatievoorbereidng.

Angular Signals en Computed in 2026: Fijnkorrelige Reactiviteit en Sollicitatievragen
Een diepgaande gids over Angular Signals, Computed Signals en het reactieve model in Angular 20+. Bevat best practices, RxJS-interoperabiliteit en veelgestelde technische sollicitatievragen.