What’s New and Different in Angular NgRX Store Version 22

A hands-on look at the changes, gotchas, and improvements I found upgrading to NgRX 22

October 16, 2026

You know that feeling when you upgrade a familiar library , and suddenly your app behaves slightly differently? No errors, no big crashes, just subtle changes that make you scratch your head?

That’s exactly what happened when I upgraded an Angular app from NgRX 13 to NgRX 22 recently. I rely on NgRX store for state management and effects, but version 22 introduced some tweaks that changed how selectors behave, typing improves, and runtime performance shifts.

Here’s a walkthrough of the key changes I encountered, what’s really going on under the hood, and how you can spot and adapt to these differences.

The NgRX store you thought you knew

If you’ve used NgRX store before, you know it’s basically Redux for Angular, with selectors, actions, reducers, and effects to manage state in a reactive way.

The core concepts stayed the same in v22, but the implementation details and typings improved. Still, some subtle differences sneaked in that can trip you up.

For example, the way selectors memoize results and how they handle parameterized selectors got some internal revisions. Also, there’s better TypeScript inference for feature selectors now.

What caught me off guard: Selector memoization changes

In previous versions, selector memoization was straightforward: a selector caches its last input and output, and skips recalculations if inputs are strictly equal.

With NgRX 22, the selector memoization logic was refined to improve performance and avoid stale data. It now better tracks dependencies internally and avoids unnecessary recalculations in some edge cases.

Sounds great until you hit this weird bug where a selector that depended on a nested object stopped updating when the nested object’s properties changed but the reference didn’t.

Here’s a quick example:

const selectFeature = createFeatureSelector<MyFeatureState>('feature');
const selectNestedValue = createSelector(
  selectFeature,
  feature => feature.nested.value
);

If your reducer mutated nested in place (which you shouldn’t do, but sometimes happens), the selector might not re-run because the top-level feature object reference didn’t change.

NgRX 22 memoizes more aggressively and relies on inputs’ references for change detection more strictly. This pushed me to audit reducers and ensure immutable updates everywhere.

Better typing for feature selectors

One of the biggest quality-of-life improvements in NgRX 22 is how TypeScript infers types for feature selectors.

Previously, you had to manually specify state interfaces frequently, or deal with loose typings that made autocomplete and error detection weaker.

Now, createFeatureSelector fully infers the feature slice type if you pass a generic argument, and downstream selectors get tighter typings automatically. This made refactoring safer and helped catch bugs early in my IDE.

Here’s how I updated selectors:

interface MyFeatureState {
  items: Item[];
  loading: boolean;
}

const selectFeature = createFeatureSelector<MyFeatureState>('myFeature');
const selectItems = createSelector(
  selectFeature,
  state => state.items
);

With version 22, the state argument in the selector callback is correctly typed as MyFeatureState, so no more casting or guessing.

Breaking changes to note: StoreModule.forRoot and forFeature

The NgRX team cleaned up the StoreModule APIs slightly. While the old syntax still works, it now warns if you mix incompatible configurations.

If your app registers feature states dynamically or with lazy-loaded modules, make sure your forFeature calls match the expected typings and that you pass reducers directly or as a map object.

They also recommend using the new provideState() and provideStore() APIs introduced alongside v22 for better tree-shaking and standalone API support in Angular 16+. Migrating to these new providers can improve bundle sizes but needs some code changes.

Effects improvements and subtle runtime behavior changes

NgRX Effects also got some polish. The main change I noticed was around error handling and completion behavior:

  • Effects don’t complete by default anymore, which means they keep listening indefinitely unless explicitly completed.
  • The new createEffect options let you specify if an effect should dispatch actions or not more clearly.

This helped me catch some subtle bugs where effects stopped firing after certain errors or completions in older versions.

Why these changes matter: A real debugging story

I spent a frustrating afternoon debugging an issue where a selector stopped updating after a certain action dispatched. The UI stayed stale.

Turns out, the reducer was mutating nested state properties instead of returning new objects. NgRX 22’s stricter memoization ignored the change because the feature slice’s root reference didn’t change.

Fixing the reducer to return new objects fixed the selector updates immediately:

// Bad (mutating nested state)
state.nested.value = newValue;
return state;

// Good (immutable update)
return {
  ...state,
  nested: {
    ...state.nested,
    value: newValue
  }
};

This reinforced the immutable update pattern more strictly than before.

Wrapping up the NgRX 22 upgrade

Upgrading to NgRX 22 was mostly smooth, but it nudged me to pay closer attention to immutability and typing.

  • Selector memoization is more aggressive and reference-based.
  • Typings for feature selectors got much better.
  • StoreModule APIs have new providers and recommended usage for Angular standalone.
  • Effects behavior around completion and dispatch is clearer now.

If you’re upgrading, audit your reducers for immutable patterns, update your selectors to use new typings, and consider switching to the new provider APIs.

NgRX store is still the solid choice for Angular state management, but version 22 reminds you to keep your state immutable and your typings tight.

If you’ve run into other quirks or hidden gems in NgRX 22, I’d love to hear about them!