<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Michael Small</title>
    <description>The latest articles on DEV Community by Michael Small (@michael-small-dev).</description>
    <link>https://dev.to/michael-small-dev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3707705%2F9a8942ef-b92a-466b-b004-1beb546d9d9d.jpg</url>
      <title>DEV Community: Michael Small</title>
      <link>https://dev.to/michael-small-dev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/michael-small-dev"/>
    <language>en</language>
    <item>
      <title>NgRx Toolkit v21</title>
      <dc:creator>Michael Small</dc:creator>
      <pubDate>Mon, 19 Jan 2026 17:20:52 +0000</pubDate>
      <link>https://dev.to/ngrx-toolkit/ngrx-toolkit-v21-4l46</link>
      <guid>https://dev.to/ngrx-toolkit/ngrx-toolkit-v21-4l46</guid>
      <description>&lt;h1&gt;
  
  
  NgRx Toolkit v21
&lt;/h1&gt;

&lt;p&gt;The NgRx Toolkit originates as far back as when the SignalStore was not even marked stable. As the package was in its early stages, community requests for various functionality poured in. However, the NgRx team wanted to focus on core functionality that would be broadly usable and supported to their full standard. But they provided the tools for the community to make its own tools, the &lt;code&gt;signalStoreFeature&lt;/code&gt; function. The powerful extensibility of the SignalStore was the reason why the NgRx Toolkit team decided to start with some features that they encountered in everyday-life programming.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;withStorageSync&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular-architects/ngrx-toolkit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;withDevtools&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular-architects/ngrx-toolkit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;UserStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signalStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;withState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;John&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;

  &lt;span class="c1"&gt;// Automatically synchronizes state to localStorage &lt;/span&gt;
  &lt;span class="c1"&gt;// on each change via the key 'user'&lt;/span&gt;
  &lt;span class="c1"&gt;// (also can do session storage and IndexedDB)&lt;/span&gt;
  &lt;span class="nf"&gt;withStorageSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;

  &lt;span class="c1"&gt;// Allows Redux Devtools (even without Redux!)&lt;/span&gt;
  &lt;span class="nf"&gt;withDevtools&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The NgRx Toolkit sees itself as rich set of extensions that you need in typical Angular applications. Core functionality, and its history:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;withDevtools()&lt;/code&gt; that allows any SignalStore, redux/events based or not at all, to use the Redux Devtools. Your SignalStore state
can be visualized in the widely used plugin by just adding the &lt;code&gt;withDevtools('storeNameHere')&lt;/code&gt; to a store.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@ngrx/signals/event&lt;/code&gt; and &lt;code&gt;withFeature&lt;/code&gt;, now in the core of &lt;code&gt;@ngrx/signals&lt;/code&gt;, were incubated with their predecessors in the toolkit.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;withStorageSync()&lt;/code&gt; for synchronizing state with Web Storage (&lt;code&gt;localStorage&lt;/code&gt;/&lt;code&gt;sessionStorage&lt;/code&gt;) and IndexedDB (via an async strategy).
As easy as &lt;code&gt;withStorageSync('storeNameHere')&lt;/code&gt;. IndexedDB was added last year by a &lt;a href="https://github.com/angular-architects/ngrx-toolkit/pull/134" rel="noopener noreferrer"&gt;community contribution&lt;/a&gt; by &lt;a href="https://github.com/mzkmnk" rel="noopener noreferrer"&gt;GitHub user mzkmnk&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Other features can be found in the documentation: &lt;a href="https://ngrx-toolkit.angulararchitects.io/docs/extensions" rel="noopener noreferrer"&gt;https://ngrx-toolkit.angulararchitects.io/docs/extensions&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fuser-attachments%2Fassets%2F4dcc0b0f-27e4-4e95-ac0e-24dcd06716c0" class="article-body-image-wrapper"&gt;&lt;img alt="Redux Devtools showing application state from withStorageSync()" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fuser-attachments%2Fassets%2F4dcc0b0f-27e4-4e95-ac0e-24dcd06716c0" width="2204" height="1464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  But first: v20 minor features: &lt;code&gt;withResource&lt;/code&gt;/&lt;code&gt;withEntityResources&lt;/code&gt;/Mutations
&lt;/h2&gt;

&lt;p&gt;Before talking about v21, there were two new features from v20 Toolkit minor versions: &lt;code&gt;withResource()&lt;/code&gt; and its entity equivalent, &lt;code&gt;withEntityResources()&lt;/code&gt;, as well as the Mutations API. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;withResource&lt;/code&gt; is a feature that connects Angular's Resource API with the store. The idea: you can use a store to directly manage async data (like loading from an API), and &lt;code&gt;withResource()&lt;/code&gt; helps you wire that in. Features unnamed and named variants. &lt;code&gt;withEntityResources&lt;/code&gt; provides the same functionality but for &lt;code&gt;@ngrx/signals/entities&lt;/code&gt; based stores.&lt;/p&gt;

&lt;p&gt;The Mutations API came in a later minor, providing the other pieces of the REST experience that mutations do not cover. Mutations come as either standalone functions (&lt;code&gt;httpMutation&lt;/code&gt;/&lt;code&gt;rxMutation&lt;/code&gt;), as well as in a &lt;code&gt;withMutation&lt;/code&gt; feature. The API was inspired by &lt;a href="https://tanstack.com/query/latest/docs/framework/angular/guides/mutations" rel="noopener noreferrer"&gt;Angular Query&lt;/a&gt; and &lt;a href="https://github.com/markostanimirovic/rx-resource-proto" rel="noopener noreferrer"&gt;Marko Stanimirović's proposed mutations API&lt;/a&gt; for Angular. We also had internal discussions with Alex Rickabaugh on our design.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;httpMutation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rxMutation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;withMutations&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular-architects/ngrx-toolkit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;withResource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;withEntityResources&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular-architects/ngrx-toolkit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;UserStore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signalStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nf"&gt;withState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="nf"&gt;withResource&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;httpResource&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`/user/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
  &lt;span class="p"&gt;})),&lt;/span&gt;
  &lt;span class="nf"&gt;withMutations&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;saveUserDetail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;rxMutation&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;operation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;userService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;saveUserDetail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;onSuccess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;onError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="na"&gt;saveToServer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;httpMutation&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;_&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`https://httpbin.org/post`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="na"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;UserResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;onSuccess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;onError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ...&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;})),&lt;/span&gt;
  &lt;span class="nf"&gt;withEntityResources&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;[]),&lt;/span&gt; &lt;span class="na"&gt;defaultValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;})),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  v21
&lt;/h2&gt;

&lt;p&gt;Finally, actual Toolkit v21 notes. Future posts that are not our debut blogpost for the library won't always have a three act structure with a detailed backstory, we promise.&lt;/p&gt;

&lt;p&gt;The three major items for v21 tie into topics we already discussed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Better error handling for &lt;code&gt;withResource()&lt;/code&gt; and &lt;code&gt;withEntityResources()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Events integration into the devtools&lt;/li&gt;
&lt;li&gt;Introduce &lt;code&gt;clearUndoRedo&lt;/code&gt; in favor of &lt;code&gt;store.clearStack&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Upgraded &lt;code&gt;withResource()&lt;/code&gt; and &lt;code&gt;withEntityResources()&lt;/code&gt; error handling
&lt;/h3&gt;

&lt;p&gt;The error throwing behavior of Angular's resources proved tricky for the signal store. Deadlock scenario: once a resource is in an error state and we update a signal in &lt;code&gt;params&lt;/code&gt;, the update calls &lt;code&gt;patchState&lt;/code&gt;, which will again access the value of the state.&lt;/p&gt;

&lt;p&gt;After a lot of experimentation, as well as discussion with various members of the Angular community, we arrived on an error handling strategy that gives Toolkit users a few options for &lt;code&gt;withResource()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ErrorHandling&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;native&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;previous value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;withResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resolver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AddressResolver&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;resolver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="c1"&gt;// Other values: 'native' and 'previous value'&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;errorHandling&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// default if not specified&lt;/span&gt;
&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;'undefined value'&lt;/code&gt; (default). In the event of an error, the resource's value will be &lt;code&gt;undefined&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;'previous value'&lt;/code&gt;. Provided the resource had a previous value, that previous value will be returned. If not, an error is thrown.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;'native'&lt;/code&gt;. No special handling is provided, inline with default error behavior.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For &lt;code&gt;withEntityResources()&lt;/code&gt;, it uses &lt;code&gt;'undefined value'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Under the hood, &lt;code&gt;'previous value'&lt;/code&gt; and &lt;code&gt;'undefined value'&lt;/code&gt; proxy the value. For a detailed explanation for why this is done, check out the &lt;a href="https://github.com/angular-architects/ngrx-toolkit/blob/main/libs/ngrx-toolkit/src/lib/with-resource.ts#L402" rel="noopener noreferrer"&gt;JSDoc for the error handling strategy&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Events integration into devtools
&lt;/h3&gt;

&lt;p&gt;There is some irony with this. The NgRx Toolkit brought events to the SignalStore before there was an official plugin, and the Toolkit provides Redux Devtools integration, with or without redux used. However, the now official NgRx events feature as it shaped up did not translate directly to working with the Toolkit's &lt;code&gt;withDevtools&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In NgRx Toolkit v21, we are fixing this with &lt;code&gt;withTrackedReducer()&lt;/code&gt;, an alternative approach to tracking reducer-based state changes in Redux DevTools.&lt;/p&gt;

&lt;p&gt;To use it&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;em&gt;Replace usages of &lt;code&gt;withReducer&lt;/code&gt; with &lt;code&gt;withTrackedReducer&lt;/code&gt;&lt;/em&gt;. Note: native &lt;code&gt;withReducer&lt;/code&gt; support is planned for the future, but that requires upstream support from &lt;code&gt;@ngrx/signals&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;&lt;code&gt;withGlitchTracking&lt;/code&gt; must be specified within &lt;code&gt;withDevtools&lt;/code&gt;&lt;/em&gt;. If &lt;code&gt;withTrackedReducer&lt;/code&gt; is used without the devtools and glitch tracking, runtime errors will be thrown.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Must have all three, or runtime errors for thee!&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;withTrackedReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;withGlitchTracking&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;withDevtools&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular-architects/ngrx-toolkit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bookEvents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;eventGroup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Book Store&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;loadBooks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signalStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nf"&gt;withDevtools&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;book-store-events&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;withGlitchTracking&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
  &lt;span class="nf"&gt;withState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;books&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Book&lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="nf"&gt;withTrackedReducer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="c1"&gt;// `[Book Store] loadBooks` will show up in the devtools&lt;/span&gt;
    &lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bookEvents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loadBooks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;books&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;mockBooks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})),&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nf"&gt;withHooks&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nf"&gt;onInit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;injectDispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bookEvents&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;loadBooks&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Introduce &lt;code&gt;clearUndoRedo&lt;/code&gt; in favor of &lt;code&gt;store.clearStack&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;clearStack&lt;/code&gt; has been deprecated in favor of a new standalone function &lt;code&gt;clearUndoRedo&lt;/code&gt;, which does a soft reset (not setting the state to &lt;code&gt;null&lt;/code&gt;) by default. &lt;/p&gt;

&lt;p&gt;The hard reset can be set via options, &lt;code&gt;clearUndoRedo(store, { lastRecord: null })&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This change was written by &lt;a href="https://x.com/GregOnNet" rel="noopener noreferrer"&gt;Gregor Woiwode&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This change was also backported to NgRx Toolkit v19.5.0 and v20.7.0.&lt;/p&gt;

&lt;h2&gt;
  
  
  ngrx-toolkit-openapi-gen
&lt;/h2&gt;

&lt;p&gt;We got a fantastic Christmas present from &lt;a href="https://x.com/wolfmanfx" rel="noopener noreferrer"&gt;Murat Sari&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;An OpenAPI generator that creates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ an NgRx Signal Store&lt;/li&gt;
&lt;li&gt;✅ with Resources&lt;/li&gt;
&lt;li&gt;✅ and Mutations&lt;/li&gt;
&lt;li&gt;✅ based on a Zod schema&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On top of that, the generated code is genuinely beautiful – which is not something you usually see with code generators. &lt;a href="https://www.npmjs.com/package/ngrx-toolkit-openapi-gen" rel="noopener noreferrer"&gt;Check it out on npm&lt;/a&gt;, as well as &lt;a href="https://coderabbit-gmbh.github.io/ngrx-toolkit-openapi-gen/" rel="noopener noreferrer"&gt;its documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future release strategy for compatible Angular versions
&lt;/h2&gt;

&lt;p&gt;v21 for the Toolkit took some time due to some obstacles with the new features and fixes. However, v21 of NgRx itself was compatible with the Toolkit's later v20 releases, but only with certain overrides. To bridge the gap for an easier user experience, we released NgRx Toolkit v20.6.0, which supported both NgRx v20 and v21. The same is applicable for v20.7.0, which contains some backports of features worked on for v21.&lt;/p&gt;

&lt;p&gt;Going forward, if there are obstacles for any coming major release of the Toolkit, we will release a minor of the Toolkit that is compatible with the next stable major of NgRx once that is available and ready to integrate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Thank you!
&lt;/h2&gt;

&lt;p&gt;We are thankful for everyone who contributes their time and expertise to discussions and code alike, as well as our users. &lt;/p&gt;

&lt;p&gt;Though we have had various contributors, here is another thanks to the highlighted contributions from this article: thank you to &lt;a href="https://github.com/mzkmnk" rel="noopener noreferrer"&gt;mzkmnk&lt;/a&gt;, &lt;a href="https://x.com/wolfmanfx" rel="noopener noreferrer"&gt;Murat Sari&lt;/a&gt;, and &lt;a href="https://x.com/GregOnNet" rel="noopener noreferrer"&gt;Gregor Woiwode&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>angular</category>
      <category>ngrx</category>
    </item>
  </channel>
</rss>
