Upgrade to Pro — share decks privately, control downloads, hide ads and more …

NgRx SignalStore: The Power of Extensibility

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.

NgRx SignalStore: The Power of Extensibility

NgRx is the undisputed standard for State Management in the Angular ecosystem. With the SignalStore, we have the latest evolution: fully optimized for Signals and officially recommended by the Angular Team.

But for me, the main selling point of the SignalStore is not just that it manages state. It is its Extensibility.

In this talk, I will give a short introduction to the SignalStore before shifting focus to its "crown jewels": Extensions. We will explore what they are, how they work under the hood, and why this modular architecture makes the SignalStore a powerful tool that belongs in every modern Angular application.

Avatar for Rainer Hahnekamp

Rainer Hahnekamp

April 14, 2026

More Decks by Rainer Hahnekamp

Other Decks in Technology

Transcript

  1. Some Facts NgRx in every 5th Angular app SignalStore -

    unified unified successor of @ngrx/store & @ngrx/component-store
  2. Some Facts NgRx in every 5th Angular app SignalStore -

    unified unified successor of @ngrx/store & @ngrx/component-store Close collaboration with the Angular team
  3. A Comparison: State & Slices @Injectable({ providedIn: 'root' }) export

    class HolidaysStore { // State readonly #state = signal<HolidayState>({ holidays: [], favouriteIds: [], filter: { query: '', type: 0 }, }); // Slices holidays = computed(() => this.#state().holidays); favouriteIds = computed(() => this.#state().favouriteIds); filter = computed(() => this.#state().filter); }
  4. A Comparison: State & Slices @Injectable({ providedIn: 'root' }) export

    class HolidaysStore { // State readonly #state = signal<HolidayState>({ holidays: [], favouriteIds: [], filter: { query: '', type: 0 }, }); // Slices holidays = computed(() => this.#state().holidays); favouriteIds = computed(() => this.#state().favouriteIds); filter = computed(() => this.#state().filter); } export const HolidayStore = signalStore( { providedIn: 'root' }, withState<HolidayState>({ holidays: [], favouriteIds: [] , filter: { query: '', type: 0 }, }) );
  5. A Comparison: State & Slices @Injectable({ providedIn: 'root' }) export

    class HolidaysStore { // State readonly #state = signal<HolidayState>({ holidays: [], favouriteIds: [], filter: { query: '', type: 0 }, }); // Slices holidays = computed(() => this.#state().holidays); favouriteIds = computed(() => this.#state().favouriteIds); filter = computed(() => this.#state().filter); } export const HolidayStore = signalStore( { providedIn: 'root' }, withState<HolidayState>({ holidays: [], favouriteIds: [] , filter: { query: '', type: 0 }, }) );
  6. A Comparison: Computeds export class HolidaysStore { // State... //

    Computeds holidaysCount = computed(() => this.#state().holidays.length); }
  7. A Comparison: Computeds export class HolidaysStore { // State... //

    Computeds holidaysCount = computed(() => this.#state().holidays.length); } export const HolidayStore = signalStore( // withState()... withComputed(({ holidays }) => ({ holidaysCount: () => holidays().length, })), );
  8. A Comparison: Methods export class HolidaysStore { // State, Computeds...

    // Methods readonly #httpClient = inject(HttpClient); async load() { const holidays = await lastValueFrom( this.#httpClient.get<Holiday[]>('/holiday') ); this.#state.update((state) => ({ ...state, holidays })); } }
  9. A Comparison: Methods export class HolidaysStore { // State, Computeds...

    // Methods readonly #httpClient = inject(HttpClient); async load() { const holidays = await lastValueFrom( this.#httpClient.get<Holiday[]>('/holiday') ); this.#state.update((state) => ({ ...state, holidays })); } } export const HolidayStore = signalStore( // withState, withComputed,... withMethods((store, http = inject(HttpClient)) => ({ async load() { const holidays = await lastValueFrom( http.get<Holiday[]>('/holiday') ); patchState(store, { holidays }); }, })) );
  10. A Comparison: Methods export class HolidaysStore { // State, Computeds...

    // Methods readonly #httpClient = inject(HttpClient); async load() { const holidays = await lastValueFrom( this.#httpClient.get<Holiday[]>('/holiday') ); this.#state.update((state) => ({ ...state, holidays })); } } export const HolidayStore = signalStore( // withState, withComputed,... withMethods((store, http = inject(HttpClient)) => ({ async load() { const holidays = await lastValueFrom( http.get<Holiday[]>('/holiday') ); patchState(store, { holidays }); }, })) );
  11. A Comparison: Methods export class HolidaysStore { // State, Computeds...

    // Methods readonly #httpClient = inject(HttpClient); async load() { const holidays = await lastValueFrom( this.#httpClient.get<Holiday[]>('/holiday') ); this.#state.update((state) => ({ ...state, holidays })); } } export const HolidayStore = signalStore( // withState, withComputed,... withMethods((store, http = inject(HttpClient)) => ({ async load() { const holidays = await lastValueFrom( http.get<Holiday[]>('/holiday') ); patchState(store, { holidays }); }, })) );