How to Persist State with Redux Persist in Redux Toolkit React Applications

Learn to implement automatic state persistence that survives page refreshes, browser restarts, and user sessions using Redux Persist with Redux Toolkit.

Understanding the State Persistence Challenge

Every React developer has experienced the frustration of building a complex form, adding items to a shopping cart, or filling out a detailed survey only to accidentally refresh the page and lose everything. This common pain point stems from how web browsers handle state by default--memory-based state disappears when the page reloads.

Redux Persist solves this problem by automatically saving your Redux store to persistent storage and restoring it when users return. This guide explores how to implement state persistence in React applications using Redux Persist with Redux Toolkit.

Why Redux Persist Matters

Key capabilities for building better user experiences

Automatic Persistence

Monitors state changes and automatically persists to storage without manual synchronization code

Session Recovery

Restores exact application state when users return, maintaining context across sessions

Flexible Storage

Supports localStorage, sessionStorage, IndexedDB, and custom storage backends

Selective Persistence

Blacklist and whitelist configurations give fine-grained control over what gets persisted

Data Transforms

Transform data before saving and after loading for encryption, compression, or filtering

Version Migrations

Handle schema changes between app versions without breaking existing user data

Installing and Setting Up Redux Persist

Installation

First, install the Redux Persist package alongside your existing Redux dependencies:

npm install redux-persist
# or
yarn add redux-persist

Basic Store Configuration

Configure your Redux store with persistReducer and persistStore:

import { configureStore } from '@reduxjs/toolkit';
import {
 persistStore,
 persistReducer,
 FLUSH,
 REHYDRATE,
 PAUSE,
 PERSIST,
 PURGE,
 REGISTER,
} from 'redux-persist';
import storage from 'redux-persist/lib/storage';
import rootReducer from './reducers';

const persistConfig = {
 key: 'root',
 storage,
};

const persistedReducer = persistReducer(persistConfig, rootReducer);

export const store = configureStore({
 reducer: persistedReducer,
 middleware: (getDefaultMiddleware) =>
 getDefaultMiddleware({
 serializableCheck: {
 ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
 },
 }),
});

export const persistor = persistStore(store);

As demonstrated in LogRocket's comprehensive Redux Persist tutorial, this configuration wraps your reducer with persistence capabilities and sets up the persistor to manage the storage lifecycle.

Integrating with React Applications

Wrap your application with PersistGate to coordinate state rehydration:

import React from 'react';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/integration/react';
import { store, persistor } from './store';
import App from './App';

ReactDOM.render(
 <Provider store={store}>
 <PersistGate loading={<LoadingSpinner />} persistor={persistor}>
 <App />
 </PersistGate>
 </Provider>,
 document.getElementById('root')
);

The PersistGate component delays rendering until persisted state has been rehydrated, ensuring users see their restored data immediately. For applications using React with TypeScript, the same pattern applies with appropriate type annotations.

Configuring Storage Engines

Available Storage Options

Redux Persist supports multiple storage backends:

  • localStorage (default): Persists across browser sessions
  • sessionStorage: Clears when browser closes
  • IndexedDB: Greater storage capacity
  • Custom engines: For specialized requirements

Session Storage Example

import createWebStorage from 'redux-persist/lib/storage/createWebStorage';

const storage = createWebStorage('session');

const persistConfig = {
 key: 'root',
 storage,
};

Choosing the right storage engine depends on your application's requirements. For e-commerce applications with shopping carts, localStorage provides the persistence users expect. For sensitive data that should clear when the browser closes, sessionStorage offers appropriate isolation.

Selective State Persistence

Blacklist and Whitelist Configuration

Control which reducers participate in persistence:

const persistConfig = {
 key: 'root',
 storage,
 blacklist: ['auth', 'notifications'], // Never persist these
 whitelist: ['userPreferences', 'cart'], // Only persist these
};
  • blacklist: Reducers that should never be persisted
  • whitelist: Only these reducers get persisted (safer default)

Persisted State Structure

When Redux Persist saves state, it creates a structure with version tracking and timestamp metadata. Understanding this structure helps with debugging and migrations. This approach aligns with Redux Toolkit's opinionated patterns for predictable state management.

Transforms for Data Manipulation

Why Transforms Matter

Transforms modify data before saving and after loading, enabling:

  • Removing sensitive information
  • Converting complex data types
  • Compressing data to reduce storage
  • Encrypting sensitive fields

Creating Custom Transforms

import { createTransform } from 'redux-persist';

const exampleTransform = createTransform(
 // inbound: transform state before it's saved
 (inboundState, key) => {
 return {
 ...inboundState,
 sensitiveData: undefined,
 lastPersisted: new Date().toISOString(),
 };
 },
 // outbound: transform state after it's retrieved
 (outboundState, key) => {
 return {
 ...outboundState,
 data: outboundState.data || [],
 };
 },
 { whitelist: ['userData'] }
);

Transforms provide a powerful way to handle data that requires special treatment, such as removing authentication tokens or converting Date objects to strings for proper serialization.

Migrations for State Schema Evolution

Handling State Schema Changes

When your application's state structure changes, migrations transform old persisted data to new formats:

const persistConfig = {
 key: 'root',
 storage,
 version: 2, // Current state version
 migrate: (createMigrate) => createMigrate({
 0: (state) => ({
 ...state,
 // Migration from version 0 to 1
 user: state.user_old_format,
 }),
 1: (state) => ({
 ...state,
 // Migration from version 1 to 2
 cart: {
 items: state.cart_old_items_format,
 lastUpdated: null,
 },
 }),
 }),
};

Migration functions run sequentially based on version numbers, transforming state step by step. This ensures users upgrading from older versions of your application don't lose their persisted data due to schema changes.

Integrating with Redux Toolkit Query

API State Rehydration

Persist Redux Toolkit Query cached data to reduce network requests:

import { createApi } from '@reduxjs/toolkit/query/react';
import { REHYDRATE } from 'redux-persist';

const api = createApi({
 baseQuery: fetchBaseQuery({ baseUrl: '/' }),
 extractRehydrationInfo(action, { reducerPath }) {
 if (action.type === REHYDRATE) {
 return action.payload?.[reducerPath];
 }
 },
 endpoints: (builder) => ({
 // Your endpoints here
 }),
});

As documented in the Redux Toolkit persistence guide, the extractRehydrationInfo option enables seamless rehydration of cached API data. This integration reduces redundant network requests and improves perceived performance for returning users.

API Cache Persistence Considerations

While persisting API cache improves perceived performance, consider data freshness requirements for your specific use cases. Some applications may prefer to blacklist certain endpoints to ensure fresh data retrieval on each session.

Best Practices and Performance Optimization

Storage Size Management

Browsers typically limit localStorage to 5-10MB. Monitor your persisted state size and optimize structures that grow unbounded.

Throttle Configuration

Reduce storage write frequency for rapidly changing state:

const persistConfig = {
 key: 'root',
 storage,
 throttle: 2000, // Persist at most every 2 seconds
};

Performance Guidelines

  • Only persist what users need to recover
  • Use throttling for frequently changing state
  • Compress large state objects
  • Clear stale data periodically
  • Test with real data volumes

Following these React development best practices ensures your state persistence implementation doesn't negatively impact application performance.

E-commerce applications benefit from persisted carts that survive across sessions. Users return to find selected items still waiting, reducing cart abandonment and improving conversion rates. Combine with database sync for authenticated users.

Frequently Asked Questions

What storage engines does Redux Persist support?

Redux Persist supports localStorage (default), sessionStorage, IndexedDB, and custom storage engines. React Native applications can use AsyncStorage. Each engine provides the same getItem, setItem, and removeItem interface.

How do I persist only specific reducers?

Use the blacklist or whitelist configuration options. Blacklist specifies reducers to exclude from persistence, while whitelist specifies only which reducers to include. Whitelists provide safer defaults by requiring explicit inclusion.

Can I encrypt persisted data?

Yes, use transforms to encrypt data before saving and decrypt after loading. Create a transform that uses your encryption library of choice, such as crypto-js or the Web Crypto API.

How do migrations work when app versions change?

The version config option tracks schema version. Migration functions transform state from one version to the next. They run sequentially, allowing chained migrations from version 1 to 2 to 3, etc.

Does Redux Persist work with Redux Toolkit Query?

Yes, use the extractRehydrationInfo option in createApi to identify rehydration actions. This allows API cache data to persist and restore, reducing network requests on return visits.

Conclusion

Redux Persist provides a robust foundation for maintaining application state across browser sessions, transforming a common frustration into a seamless user experience. The library's configuration-driven approach keeps persistence concerns separate from your business logic, while its extensibility through transforms, storage engines, and migrations handles sophisticated requirements.

Whether you're building e-commerce platforms that preserve shopping carts, productivity tools that maintain user context, or any application where state persistence improves usability, Redux Persist offers the capabilities you need with the implementation simplicity that makes getting started straightforward.

Start with basic persistence configuration, then add transforms, migrations, and optimization as your application's persistence requirements evolve. Combined with Redux Toolkit's modern patterns, you can build React applications that provide exceptional user experiences.

State Persistence Impact

5-10MB

localStorage capacity

2000ms

Recommended throttle

6

Core lifecycle actions

Build Better React Applications

Our team specializes in React development with Redux, Redux Toolkit, and modern state management patterns that create seamless user experiences.