npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@menardi/redux-persist

v8.0.0

Published

Persist and rehydrate Redux stores

Readme

@menardi/redux-persist

npm install @menardi/redux-persist

A fork of redux-persist (forked from v6.0.0) which is focused on more reliable usage as a permanent store rather than a cache.

Main differences to redux-persist

I use this fork for my mobile apps, so my main goal is to ensure it stores data reliably inside a mobile app like React Native or Capacitor.

  • Throws errors instead of silently deleting data when something goes wrong
  • State reconciliation defaults to level 2, which means that adding new keys inside a reducer with default values works as expected
  • Support for Redux v5 and @reduxjs/toolkit

Breaking changes from redux-persist v6

  1. The stateReconciler config option has been removed, and replaced with rehydrationDepth
    • stateReconciler: autoMergeLevel1 -> rehydrationDepth: 1
    • stateReconciler: autoMergeLevel2 -> rehydrationDepth: 2
    • stateReconciler: hardSet -> removed
  2. The whitelist and blacklist config options have been renamed, but the old names are still supported for backwards compatibility
    • whitelist -> allowlist
    • blacklist -> blocklist
  3. persistCombineReducers has been removed
    • If you were using this, separately call combineReducers and persistReducer instead
  4. The redux-persist/lib/storage export has been removed
    • This offered browser-specific storage. Instead, set storage: localStorage or storage: sessionStorage directly
  5. The deprecated keyPrefix config option has been removed
  6. The createTransform function now takes an object argument
    • createTransform(in, out, { whitelist: ['myReducer'] }) -> createTransform({ reducerName: 'myReducer', onBeforePersist: in, onBeforeRehydrate: out })

Basic Usage

Basic usage involves adding persistReducer and persistStore to your setup.

// configureStore.ts
import { combineReducers, configureStore } from '@reduxjs/toolkit';
import { persistReducer, persistStore, ALL_PERSIST_ACTIONS } from '@menardi/redux-persist';

const rootReducer = combineReducers({
  // your reducers here
});

const persistedReducer = persistReducer({
  key: 'root',
  storage: MyStorage,
}, rootReducer);

export const store = configureStore({
  reducer: persistedReducer,
  middleware: (getDefaultMiddleware) => (
    getDefaultMiddleware({
      serializableCheck: {
        // This tells redux-toolkit to ignore its serializability check for redux-persist's internal actions
        ignoredActions: ALL_PERSIST_ACTIONS,
      },
    })
  ),
});

export const persistor = persistStore(store);

Usage with React

If you are using React, wrap your root component with PersistGate. This delays the rendering of your app's UI until your persisted state has been retrieved and saved to redux. NOTE the PersistGate loading prop can be null, or any react instance, e.g. loading={<Loading />}

import { PersistGate } from '@menardi/redux-persist/integration/react'

// ... normal setup, create store and persistor, import components etc.

const App = () => {
  return (
    <Provider store={store}>
      <PersistGate loading={null} persistor={persistor}>
        <RootComponent />
      </PersistGate>
    </Provider>
  );
};

Storage

Any storage which provides getItem, setItem and removeItem functions can be used for your persistor.

React Native

If you're starting a new React Native project, react-native-mmkv is the recommended storage option due to its speed and synchronous reads.

react-native-mmkv

You can create your own wrapper around react-native-mmkv to match the format that redux-persist expects.

import { createMMKV } from 'react-native-mmkv';

const mmkvStorage = createMMKV();

const PersistCompatibleMmkv = {
  setItem: (key: string, value: string) => {
    mmkvStorage.set(key, value);
  },
  getItem: (key: string) => {
    return mmkvStorage.getString(key);
  },
  removeItem: (key: string) => {
    mmkvStorage.remove(key);
  },
};

const rootReducer = combineReducers({
  // your reducers here
});

const persistedReducer = persistReducer({
  key: 'root',
  storage: PersistCompatibleMmkv,
}, rootReducer);

@react-native-async-storage/async-storage

AsyncStorage already provides the exact functions needed, and so can be passed directly to persistReducer as is.

import AsyncStorage from '@react-native-async-storage/async-storage';

const rootReducer = combineReducers({
  // your reducers here
});

const persistedReducer = persistReducer({
  key: 'root',
  storage: AsyncStorage,
}, rootReducer);

Capacitor

@capacitor/preferences

You can create your own wrapper around @capacitor/preferences to match the format that redux-persist expects.

import { Preferences } from '@capacitor/preferences'

const PersistCompatiblePreferences = {
  setItem: (key: string, value: string) => {
    return Preferences.set({ key, value });
  },
  getItem: async (key: string) => {
    const result = await Preferences.get({ key });
    return result.value;
  },
  removeItem: (key: string) => {
    return Preferences.remove({ key });
  },
};

const rootReducer = combineReducers({
  // your reducers here
});

const persistedReducer = persistReducer({
  key: 'root',
  storage: PersistCompatiblePreferences,
}, rootReducer);

Browser

localStorage

[!IMPORTANT] If you're using redux-persist in a hybrid mobile app (like Capacitor or Cordova), do not use localStorage. The iOS system treats localStorage like a cache that can be cleared, so data can be lost unexpectedly. For hybrid apps, use a native storage option, like @capacitor/preferences above.

localStorage already provides the exact functions needed, and so can be passed directly to persistReducer as is.

const rootReducer = combineReducers({
  // your reducers here
});

const persistedReducer = persistReducer({
  key: 'root',
  storage: localStorage,
}, rootReducer);

Allowlist and Blocklist

To block specific reducers from being persisted, add them to the blocklist config array. If you want to only persist specific reducers, you can use the allowlist config option.

// BLOCKLIST
const persistConfig = {
  key: 'root',
  storage: storage,
  blocklist: ['navigation'] // navigation will not be persisted
};

// ALLOWLIST
const persistConfig = {
  key: 'root',
  storage: storage,
  allowlist: ['navigation'] // only navigation will be persisted
};

Migrations

persistReducer has a general purpose "migrate" config which will be called after getting stored state but before actually reconciling with the reducer. It can be any function which takes state as an argument and returns a promise to return a new state object.

Redux Persist ships with createMigrate, which helps create a synchronous migration for moving from any version of stored state to the current state version.

Example with createMigrate

import { combineReducers, configureStore } from '@reduxjs/toolkit';
import { createMigrate, persistReducer, persistStore, ALL_PERSIST_ACTIONS } from '@menardi/redux-persist';

const rootReducer = combineReducers({
  // your reducers here
});

const migrations = {
  0: (state) => {
    // migration to delete "device"
    return {
      ...state,
      device: undefined
    }
  },
  1: (state) => {
    // migration to keep only "device"
    return {
      device: state.device
    }
  }
}

const persistedReducer = persistReducer({
  key: 'root',
  version: 1,
  storage: MyStorage,
  migrate: createMigrate(migrations),
}, rootReducer);

...

Alternative

The migrate method can be any function with which returns a promise of new state.

const persistedReducer = persistReducer({
  key: 'root',
  version: 1,
  storage: MyStorage,
  migrate: (state) => {
    console.log('Migration Running!')
    return Promise.resolve(state)
  }
}, rootReducer);

Transforms

Transforms allow you to customize the state object that gets persisted and rehydrated.

When the state object gets persisted, it first gets serialized with JSON.stringify(). If parts of your state object are not mappable to JSON objects, the serialization process may transform these parts of your state in unexpected ways. For example, the javascript Set type does not exist in JSON. When you try to serialize a Set via JSON.stringify(), it gets converted to an empty object, which is almost definitely not what you want.

Below is a Transform that successfully persists a Set property, which simply converts it to an array and back. In this way, the Set gets converted to an Array, which is a recognized data structure in JSON. When pulled out of the persisted store, the array gets converted back to a Set before being saved to the redux store.

import { createTransform } from '@menardi/redux-persist';

const rootReducer = combineReducers({
  reducerWithSet: ...
});

type RootState = ReturnType<typeof rootReducer>;

const SetTransform = createTransform<RootState, 'reducerWithSet'>({
  reducerName: 'reducerWithSet',
  onBeforePersist: (state) => {
    // Note that `state` is the state of the specified reducer (`reducerWithSet`), not the `rootReducer`
    return { ...state, mySet: [...state.mySet] };
  },
  onBeforeRehydrate: (state) => {
    return { ...state, mySet: new Set(state.mySet) };
  },
});

const persistedReducer = persistReducer({
  key: 'root',
  storage: MyStorage,
  transforms: [SetTransform]
}, rootReducer);