Skip to content
Highly configurable state syncing (with include/exclude keys) between the @ngrx/store and localstorage/sessionstorage
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.
lib updates README Mar 31, 2019
.travis.yml added husky Mar 21, 2019
LICENSE updates README Mar 31, 2019


npm-release git-release travis build license

Highly configurable state syncing between the @ngrx/store and localstorage/sessionstorage.

State sync

You can sync only the objects you need, allowing you to exclude/include deeply nested keys.
You can sync different 'feature' states to different storage locations. For example:

  • feature1 to sessionStorage
  • feature2 to localStorage


@larscom/ngrx-store-storagesync depends on @ngrx/store and Angular 2+.


npm i --save @larscom/ngrx-store-storagesync


1. Wrap storageSync in an exported function and include it in your meta-reducers array in StoreModule.forRoot

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { StoreModule, ActionReducerMap, ActionReducer, MetaReducer } from '@ngrx/store';
import { routerReducer } from '@ngrx/router-store';
import { storageSync } from '@larscom/ngrx-store-storagesync';
import * as fromFeature1 from './feature/reducer';
import * as fromFeature2 from './feature2/reducer';
import * as fromFeature3 from './feature3/reducer';

export const reducers: ActionReducerMap<IState> = {
  router: routerReducer,
  feature1: fromFeature1.reducer,
  feature2: fromFeature2.reducer,
  // feature3 does not get synced to storage at all
  feature3: fromFeature3.reducer

export function storageSyncReducer(reducer: ActionReducer<any>): ActionReducer<any> {
  return storageSync<IState>({
    features: [
      // save only router state to sessionStorage
      { stateKey: 'router', storageForFeature: window.sessionStorage },

      // exclude key 'success' inside 'auth' and key 'loading' inside 'feature1'
      { stateKey: 'feature1', excludeKeys: ['auth.success', 'loading'] },

      // only include key 'success' inside 'auth' and all keys with 'loading' inside 'feature2'
      { stateKey: 'feature2', includeKeys: ['auth.success', 'loading'] }
    // defaults to localStorage
    storage: window.localStorage

const metaReducers: Array<MetaReducer<any, any>> = [storageSyncReducer];

  imports: [BrowserModule, StoreModule.forRoot(reducers, { metaReducers })]
export class AppModule {}


By default the state gets deserialized and parsed by JSON.parse with an ISO date reviver. This means that your ISO date objects gets stored as string, and restored as Date

If you do not want this behaviour, you can implement your own deserialize function.


export interface IStorageSyncOptions<T> {
   * By default, states are not synced, provide the feature states you want to sync.
  features: Array<IFeatureOptions<T>>;
   * Provide the storage type to sync the state to, it can be any storage which implements the 'Storage' interface.
  storage: Storage;
   * Function that gets executed on a storage error
   * @param error the error that occurred
  storageError?: (error: any) => void;
   * Pull initial state from storage on startup
   * @default rehydrate true
  rehydrate?: boolean;
   * Serializer for storage keys
   * @param key the storage item key
   * @default storageKeySerializer (key: string) => key
  storageKeySerializer?: (key: string) => string;
   * Custom state merge function after rehydration (by default it does a deep merge)
   * @param state the next state
   * @param rehydratedState the state resolved from a storage location
   * @default rehydrateStateMerger (state: T, rehydratedState: T) => deepMerge(state, rehydratedState)
  rehydrateStateMerger?: (state: T, rehydratedState: T) => T;
export interface IFeatureOptions<T> {
   * The name of the feature state
  stateKey: string;
   * Filter out properties that exist on the feature state.
   * Can't be used together with includeKeys
   * @see includeKeys
   * @throws StorageSyncError if includeKeys is also present
  excludeKeys?: string[];
   * Only sync these properties on the feature state
   * Can't be used together with excludeKeys
   * @see excludeKeys
   * @throws StorageSyncError if excludeKeys is also present
  includeKeys?: string[];
   * Provide the storage type to sync the feature state to,
   * it can be any storage which implements the 'Storage' interface.
   * It will override the storage property in StorageSyncOptions
   * @see IStorageSyncOptions
  storageForFeature?: Storage;
   * Sync to storage will only occur when this function returns true
   * @param featureState the next feature state
   * @param state the next state
   * @default shouldSync(featureState: Partial<T>, state: T) => true
  shouldSync?: (featureState: Partial<T>, state: T) => boolean;
   * Serializer for storage keys (feature state),
   * it will override the global storageKeySerializer for this feature
   * @param key the storage item key
   * @default storageKeySerializerForFeature(key: string) => key
  storageKeySerializerForFeature?: (key: string) => string;
   * Serializer for the feature state (before saving to a storage location)
   * @param featureState the next feature state
   * @default serialize(featureState: Partial<T>) => JSON.stringify(featureState)
  serialize?: (featureState: Partial<T>) => string;
   * Deserializer for the feature state (after getting the state from a storage location)
   * ISO Date objects which are stored as a string gets revived as Date object by default.
   * @param featureState the feature state retrieved from a storage location
   * @default deserialize (featureState: string) => JSON.Parse(featureState)
  deserialize?: (featureState: string) => Partial<T>;
You can’t perform that action at this time.