0
0
ReactHow-ToBeginner · 4 min read

How to Persist Redux State in React Applications

To persist Redux state, use the redux-persist library which saves the state to storage like localStorage. It automatically rehydrates the state on app reload, keeping your data intact between sessions.
📐

Syntax

Use redux-persist by wrapping your Redux store with persistStore and persistReducer. Configure a persistConfig to specify storage and which parts of the state to save.

  • persistConfig: Defines storage and keys.
  • persistReducer: Enhances your root reducer to handle persistence.
  • persistStore: Creates a persistor to control persistence lifecycle.
javascript
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import { configureStore } from '@reduxjs/toolkit';
import rootReducer from './reducers';

const persistConfig = {
  key: 'root',
  storage,
  whitelist: ['someReducer'] // optional: which reducers to persist
};

const persistedReducer = persistReducer(persistConfig, rootReducer);

const store = configureStore({
  reducer: persistedReducer
});

const persistor = persistStore(store);

export { store, persistor };
💻

Example

This example shows a simple React app with Redux state persisted using redux-persist. The counter value stays saved in localStorage even after refreshing the page.

javascript
import React from 'react';
import { Provider, useDispatch, useSelector } from 'react-redux';
import { configureStore, createSlice } from '@reduxjs/toolkit';
import { persistStore, persistReducer } from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import { PersistGate } from 'redux-persist/integration/react';

// Slice with counter state
const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 },
  reducers: {
    increment: state => { state.value += 1; },
    decrement: state => { state.value -= 1; }
  }
});

const persistConfig = { key: 'root', storage };
const persistedReducer = persistReducer(persistConfig, counterSlice.reducer);

const store = configureStore({ reducer: persistedReducer });
const persistor = persistStore(store);

function Counter() {
  const count = useSelector(state => state.value);
  const dispatch = useDispatch();
  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={() => dispatch(counterSlice.actions.increment())}>+</button>
      <button onClick={() => dispatch(counterSlice.actions.decrement())}>-</button>
    </div>
  );
}

export default function App() {
  return (
    <Provider store={store}>
      <PersistGate loading={null} persistor={persistor}>
        <Counter />
      </PersistGate>
    </Provider>
  );
}
Output
Count: 0 (initially) Click + button: Count increments Refresh page: Count value remains the same
⚠️

Common Pitfalls

  • Not wrapping the app with PersistGate causes UI to render before state rehydration, leading to flicker or wrong initial state.
  • Forgetting to specify storage in persistConfig defaults to localStorage; using sessionStorage requires explicit import.
  • Persisting large or sensitive data can slow app or cause security issues; whitelist only needed reducers.
  • Not handling migrations when state shape changes can cause errors; use redux-persist versioning features.
jsx
/* Wrong: Missing PersistGate causes UI to show before state loads */
<Provider store={store}>
  <Counter />
</Provider>

/* Right: Wrap with PersistGate to delay rendering until rehydration */
<Provider store={store}>
  <PersistGate loading={null} persistor={persistor}>
    <Counter />
  </PersistGate>
</Provider>
📊

Quick Reference

ConceptDescription
persistConfigSettings for persistence like storage and keys to save
persistReducerWraps root reducer to add persistence logic
persistStoreCreates persistor to manage saving and loading state
PersistGateDelays UI rendering until persisted state is loaded
storageStorage engine, usually localStorage or sessionStorage

Key Takeaways

Use redux-persist to save and rehydrate Redux state automatically.
Wrap your root reducer with persistReducer and create a persistor with persistStore.
Wrap your app in PersistGate to delay rendering until state is loaded.
Whitelist only necessary reducers to avoid saving unwanted data.
Test persistence by refreshing the app and verifying state remains.