Photo by Sergio Capuzzimati on Unsplash
π₯ 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
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 fromsessionStorage
.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 Type | Lifetime | Data Size | Performance | Use Case |
localStorage | Until manually cleared | ~5MB | Synchronous (can block UI) | Basic state persistence |
sessionStorage | Until the tab is closed | ~5MB | Synchronous (fast) | Session-based state (e.g., form data) |
IndexedDB | Persistent | ~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.