πŸ”₯ Persisting Shared State in Angular Micro Frontends using NgRx + Session Storage / IndexedDB

πŸ”Ή Choosing the Right State Persistence for NgRx in MFEs βœ… Session Storage for temporary state (resets on tab close) βœ… IndexedDB for large, persistent

Β·

3 min read

We previously implemented localStorage-based persistence. Now, let’s explore:
βœ… SessionStorage-based persistence (State resets when the tab is closed)
βœ… IndexedDB-based persistence (For large, structured state storage)


πŸ“Œ Option 1: Using Session Storage in NgRx

πŸ”Ή Session storage is best when:

  • You want to persist state only for the active session (until the tab is closed).

  • You don’t want to keep data after the user leaves the app.


1️⃣ Create a MetaReducer for Session Storage

Modify projects/mfe1/src/app/store/session-storage.reducer.ts:

import { ActionReducer, INIT, UPDATE } from '@ngrx/store';

export function sessionStorageMetaReducer<S>(reducer: ActionReducer<S>): ActionReducer<S> {
  return (state, action) => {
    if (action.type === INIT || action.type === UPDATE) {
      const savedState = sessionStorage.getItem('appState');
      return savedState ? JSON.parse(savedState) : state;
    }

    const nextState = reducer(state, action);
    sessionStorage.setItem('appState', JSON.stringify(nextState));
    return nextState;
  };
}

πŸ” What it Does:

  • On App Start (INIT) β†’ Loads state from sessionStorage.

  • On Any Update β†’ Saves the latest state to sessionStorage.


2️⃣ Apply Session Storage MetaReducer

Modify projects/mfe1/src/app/app.module.ts:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { StoreModule, MetaReducer } from '@ngrx/store';
import { counterReducer } from './store/counter.reducer';
import { sessionStorageMetaReducer } from './store/session-storage.reducer';
import { AppComponent } from './app.component';

const metaReducers: MetaReducer<any>[] = [sessionStorageMetaReducer];

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    StoreModule.forRoot({ count: counterReducer }, { metaReducers }), // Apply Session Storage
  ],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

βœ… Now, the state resets when the tab is closed, but persists while browsing.


πŸ“Œ Option 2: Using IndexedDB in NgRx

πŸ”Ή IndexedDB is best when:

  • You need to store large amounts of structured state (e.g., user data, forms, e-commerce cart).

  • You want persistent state without blocking the main thread (better performance than localStorage).

1️⃣ Install IndexedDB Library

We’ll use idb-keyval, a simple IndexedDB wrapper:

npm install idb-keyval

2️⃣ Create an IndexedDB MetaReducer

Modify projects/mfe1/src/app/store/indexeddb.reducer.ts:

import { ActionReducer, INIT, UPDATE } from '@ngrx/store';
import { set, get } from 'idb-keyval';

export function indexedDBMetaReducer<S>(reducer: ActionReducer<S>): ActionReducer<S> {
  return async (state, action) => {
    if (action.type === INIT || action.type === UPDATE) {
      const savedState = await get('appState');
      return savedState ? savedState : state;
    }

    const nextState = reducer(state, action);
    await set('appState', nextState);
    return nextState;
  };
}

πŸ” How it Works:

  • On App Load (INIT), it asynchronously fetches state from IndexedDB.

  • On State Update, it stores the latest state in IndexedDB.


3️⃣ Apply IndexedDB MetaReducer

Modify projects/mfe1/src/app/app.module.ts:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { StoreModule, MetaReducer } from '@ngrx/store';
import { counterReducer } from './store/counter.reducer';
import { indexedDBMetaReducer } from './store/indexeddb.reducer';
import { AppComponent } from './app.component';

const metaReducers: MetaReducer<any>[] = [indexedDBMetaReducer];

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    StoreModule.forRoot({ count: counterReducer }, { metaReducers }), // Apply IndexedDB
  ],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

βœ… IndexedDB now stores the state persistently without blocking the main thread! πŸŽ‰


πŸ”₯ Which Persistence Method Should You Choose?

Storage TypeLifetimeData SizePerformanceUse Case
localStorageUntil manually cleared~5MBSynchronous (can block UI)Basic state persistence
sessionStorageUntil the tab is closed~5MBSynchronous (fast)Session-based state (e.g., form data)
IndexedDBPersistent~500MB+Asynchronous (non-blocking)Large, structured data (e.g., carts, offline apps)

πŸ”₯ Final Summary

βœ… SessionStorage β†’ Best for temporary state, resets when the tab closes.
βœ… IndexedDB β†’ Best for scalable, non-blocking persistence (recommended for large apps).
βœ… Both work well with NgRx + Micro Frontends.

Β