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 🙏

© 2025 – Pkg Stats / Ryan Hefner

redux-selective-persist

v1.0.5

Published

A lightweight, flexible Redux state persistence library with selective slice persistence and conditional storage. Supports Web (localStorage) and React Native (AsyncStorage).

Readme

redux-selective-persist

A lightweight, flexible Redux state persistence library with selective slice persistence and conditional storage. Zero dependencies (except Redux peer deps), full TypeScript support, and framework-agnostic. Now with React Native support!

npm version License: MIT

Why redux-selective-persist?

Unlike redux-persist which can be complex and heavyweight, redux-selective-persist offers:

  • Lightweight - Only ~1KB minified and gzipped
  • Simple API - Just 3 functions: saveState, loadState, clearState
  • Selective Persistence - Choose exactly which slices to persist
  • Conditional Persistence - Persist based on app state (e.g., only when logged in)
  • Custom Storage - Use localStorage, sessionStorage, or bring your own
  • React Native Support - Full AsyncStorage support out of the box
  • Type Safe - Full TypeScript support with generics
  • Zero Config - Works out of the box with sensible defaults
  • Framework Agnostic - Works with any Redux setup
  • No Throttling by Default - Saves immediately unless you configure throttling

Installation

Web (React)

npm install redux-selective-persist

React Native

npm install redux-selective-persist @react-native-async-storage/async-storage

or

yarn add redux-selective-persist
# For React Native also add:
yarn add @react-native-async-storage/async-storage

Table of Contents

Quick Start

Web Usage

Minimal Setup (Persist Everything)

import { configureStore } from "@reduxjs/toolkit";
import { createPersist } from "redux-selective-persist";
import rootReducer from "./reducers";

// Create persist instance
const persist = createPersist();

// Load persisted state
const preloadedState = persist.loadState();

// Create store with persisted state
export const store = configureStore({
  reducer: rootReducer,
  preloadedState,
});

// Subscribe to state changes
store.subscribe(() => {
  persist.saveState(store.getState());
});

Selective Persistence (Recommended)

import { configureStore } from "@reduxjs/toolkit";
import { createPersist } from "redux-selective-persist";
import rootReducer from "./reducers";

// Only persist specific slices
const persist = createPersist({
  slices: ["user", "settings", "cart"], // Only these will be persisted
});

const preloadedState = persist.loadState();

export const store = configureStore({
  reducer: rootReducer,
  preloadedState,
});

store.subscribe(() => {
  persist.saveState(store.getState());
});

React Native Usage

React Native requires async storage, so use the createAsyncPersist API:

Basic React Native Setup

import { configureStore } from "@reduxjs/toolkit";
import {
  createAsyncPersist,
  createAsyncStorageEngine,
} from "redux-selective-persist";
import AsyncStorage from "@react-native-async-storage/async-storage";
import rootReducer from "./reducers";

// Create async storage engine
const storage = createAsyncStorageEngine(AsyncStorage);

// Create async persist instance
const persist = createAsyncPersist({
  storage,
  slices: ["user", "settings", "cart"],
});

// Load persisted state asynchronously
const initializeStore = async () => {
  const preloadedState = await persist.loadStateAsync();

  const store = configureStore({
    reducer: rootReducer,
    preloadedState,
  });

  // Subscribe to state changes
  store.subscribe(() => {
    persist.saveStateAsync(store.getState());
  });

  return store;
};

// Use in your app
export const store = await initializeStore();

React Native with Redux Toolkit Listener Middleware

import { configureStore } from "@reduxjs/toolkit";
import {
  createAsyncPersist,
  createAsyncStorageEngine,
} from "redux-selective-persist";
import AsyncStorage from "@react-native-async-storage/async-storage";
import rootReducer from "./reducers";

const storage = createAsyncStorageEngine(AsyncStorage);
const persist = createAsyncPersist({
  storage,
  slices: ["user", "settings"],
});

export const setupStore = async () => {
  const preloadedState = await persist.loadStateAsync();

  const store = configureStore({
    reducer: rootReducer,
    preloadedState,
  });

  // Auto-save on every state change
  let previousState = store.getState();
  store.subscribe(() => {
    const currentState = store.getState();
    if (currentState !== previousState) {
      persist.saveStateAsync(currentState);
      previousState = currentState;
    }
  });

  return store;
};

Basic Examples

Example 1: Persist Only User Preferences

import { createPersist } from "redux-selective-persist";

const persist = createPersist({
  key: "myApp_preferences",
  slices: ["theme", "language", "notifications"],
});

// In your store setup
const preloadedState = persist.loadState();
const store = configureStore({
  reducer: rootReducer,
  preloadedState,
});

store.subscribe(() => {
  persist.saveState(store.getState());
});

Example 2: Conditional Persistence (Only When Logged In)

const persist = createPersist({
  slices: ["dashboard", "preferences", "drafts"],
  shouldPersist: (state) => {
    // Only persist if user is authenticated
    return state.auth.isLoggedIn;
  },
});

// In your logout action, clear persisted data
export const logout = () => (dispatch) => {
  persist.clearState(); // Clear all persisted state
  dispatch({ type: "auth/logout" });
};

Example 3: Using Session Storage (Clears on Tab Close)

import { createPersist, sessionStorage } from "redux-selective-persist";

const persist = createPersist({
  storage: sessionStorage, // Use session storage instead of localStorage
  slices: ["temporaryFilters", "searchResults"],
});

Example 4: Custom Storage Key for Multiple Environments

const environment = process.env.NODE_ENV;

const persist = createPersist({
  key: `myApp_${environment}_state`, // Different keys for dev/staging/prod
  slices: ["user", "settings"],
});

Example 5: Persist Shopping Cart

const cartPersist = createPersist({
  key: "shopping_cart",
  slices: ["cart"],
  // Optional: Add expiration logic
  serialize: (state) => ({
    ...state,
    expiresAt: Date.now() + 7 * 24 * 60 * 60 * 1000, // 7 days
  }),
  deserialize: (state) => {
    // Check if cart has expired
    if (state.expiresAt && Date.now() > state.expiresAt) {
      return { cart: { items: [] } }; // Return empty cart
    }
    return state;
  },
});

Advanced Examples

Example 6: State Version Migration

const persist = createPersist({
  version: 3,
  slices: ["user", "settings"],
  migrate: (persistedState, currentVersion) => {
    // Handle migrations between versions
    if (currentVersion === 2) {
      // Migrate from v1 to v2
      return {
        ...persistedState,
        settings: {
          ...persistedState.settings,
          theme: persistedState.settings.darkMode ? "dark" : "light",
        },
      };
    }

    if (currentVersion === 3) {
      // Migrate from v2 to v3
      return {
        ...persistedState,
        user: {
          ...persistedState.user,
          preferences: {
            ...persistedState.user.preferences,
            notifications: true,
          },
        },
      };
    }

    return persistedState;
  },
});

Example 7: Remove Sensitive Data Before Saving

const persist = createPersist({
  slices: ["auth", "user"],
  serialize: (state) => {
    // Remove sensitive data before persisting
    const safeToPersist = { ...state };

    if (safeToPersist.auth) {
      const { password, creditCard, ...safeAuth } = safeToPersist.auth;
      safeToPersist.auth = safeAuth;
    }

    if (safeToPersist.user?.paymentMethods) {
      delete safeToPersist.user.paymentMethods;
    }

    return safeToPersist;
  },
});

Example 8: Throttled/Debounced Saves for Performance

import { createPersist, createThrottle } from "redux-selective-persist";

const persist = createPersist({
  slices: ["editor", "draft"],
  throttle: createThrottle(2000), // Save at most once every 2 seconds
});

Example 9: Custom Throttle Implementation

const persist = createPersist({
  slices: ["form", "editor"],
  throttle: (saveFn) => {
    let timeout: NodeJS.Timeout | null = null;
    let lastSave = 0;
    const MIN_INTERVAL = 3000; // Minimum 3 seconds between saves

    return (state) => {
      const now = Date.now();
      const timeSinceLastSave = now - lastSave;

      if (timeout) clearTimeout(timeout);

      if (timeSinceLastSave >= MIN_INTERVAL) {
        // Save immediately if enough time has passed
        saveFn(state);
        lastSave = now;
      } else {
        // Otherwise, schedule a save
        timeout = setTimeout(() => {
          saveFn(state);
          lastSave = Date.now();
        }, MIN_INTERVAL - timeSinceLastSave);
      }
    };
  },
});

Example 10: Using Memory Storage for Testing

import { createPersist, createMemoryStorage } from "redux-selective-persist";

// In your test setup
const mockStorage = createMemoryStorage();

const persist = createPersist({
  storage: mockStorage,
  slices: ["user"],
});

// Now you can test persistence without affecting localStorage

Example 11: Custom Storage Engine (IndexedDB)

// Example IndexedDB storage engine
const indexedDBStorage = {
  getItem: async (key: string) => {
    const db = await openDB("myApp", 1);
    const value = await db.get("state", key);
    return value ? JSON.stringify(value) : null;
  },
  setItem: async (key: string, value: string) => {
    const db = await openDB("myApp", 1);
    await db.put("state", JSON.parse(value), key);
  },
  removeItem: async (key: string) => {
    const db = await openDB("myApp", 1);
    await db.delete("state", key);
  },
};

const persist = createPersist({
  storage: indexedDBStorage,
  slices: ["largeDataset"],
});

Example 12: React Native with AsyncStorage

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

const asyncStorage = {
  getItem: (key: string) => AsyncStorage.getItem(key),
  setItem: (key: string, value: string) => AsyncStorage.setItem(key, value),
  removeItem: (key: string) => AsyncStorage.removeItem(key),
};

const persist = createPersist({
  storage: asyncStorage,
  slices: ["user", "settings"],
});

Example 13: Debug Mode for Development

const persist = createPersist({
  debug: process.env.NODE_ENV === "development",
  slices: ["user", "cart", "settings"],
});

// Console output when debug is true:
// [redux-selective-persist] State saved successfully { slices: ['user', 'cart'], version: undefined }
// [redux-selective-persist] State loaded successfully { version: undefined }

Example 14: Encrypt Data Before Saving

import CryptoJS from "crypto-js";

const SECRET_KEY = "your-secret-key";

const persist = createPersist({
  slices: ["sensitiveData"],
  serialize: (state) => {
    const encrypted = CryptoJS.AES.encrypt(
      JSON.stringify(state),
      SECRET_KEY
    ).toString();
    return { encrypted };
  },
  deserialize: (state) => {
    if (state.encrypted) {
      const decrypted = CryptoJS.AES.decrypt(
        state.encrypted,
        SECRET_KEY
      ).toString(CryptoJS.enc.Utf8);
      return JSON.parse(decrypted);
    }
    return state;
  },
});

Example 15: Compress Large State

import pako from "pako";

const persist = createPersist({
  slices: ["largeDataSet"],
  serialize: (state) => {
    const json = JSON.stringify(state);
    const compressed = pako.deflate(json);
    return { compressed: btoa(String.fromCharCode(...compressed)) };
  },
  deserialize: (state) => {
    if (state.compressed) {
      const binary = atob(state.compressed);
      const bytes = new Uint8Array(binary.length);
      for (let i = 0; i < binary.length; i++) {
        bytes[i] = binary.charCodeAt(i);
      }
      const decompressed = pako.inflate(bytes, { to: "string" });
      return JSON.parse(decompressed);
    }
    return state;
  },
});

Example 16: Multiple Persistence Instances

// Persist user data with one key
const userPersist = createPersist({
  key: "user_data",
  slices: ["user", "profile"],
});

// Persist app settings with another key
const settingsPersist = createPersist({
  key: "app_settings",
  slices: ["theme", "language", "notifications"],
});

// Persist temporary data in session storage
const tempPersist = createPersist({
  key: "temp_data",
  storage: sessionStorage,
  slices: ["filters", "searchQuery"],
});

// In store setup
const preloadedState = {
  ...userPersist.loadState(),
  ...settingsPersist.loadState(),
  ...tempPersist.loadState(),
};

store.subscribe(() => {
  const state = store.getState();
  userPersist.saveState(state);
  settingsPersist.saveState(state);
  tempPersist.saveState(state);
});

Example 17: Conditional Persistence Based on User Subscription

const persist = createPersist({
  slices: ["premiumFeatures", "advancedSettings"],
  shouldPersist: (state) => {
    // Only persist if user has premium subscription
    return state.user.subscription?.isPremium === true;
  },
});

Example 18: Clear Specific Data on Logout

const persist = createPersist({
  slices: ["user", "preferences", "drafts"],
});

// In your logout reducer
const authSlice = createSlice({
  name: "auth",
  initialState,
  reducers: {
    logout: (state) => {
      // Clear persisted state on logout
      persist.clearState();

      // Reset auth state
      state.isLoggedIn = false;
      state.user = null;
      state.token = null;
    },
  },
});

API Reference

Web API (Synchronous)

createPersist<S>(config?: PersistConfig<S>)

Creates a persistence manager with automatic state saving and loading for web applications.

Returns: { saveState, loadState, clearState }

Configuration Options

| Option | Type | Default | Description | | --------------- | ------------------------- | ----------------------- | --------------------------------------------------------------------------- | | key | string | 'persistedReduxState' | Storage key used to store state | | slices | string[] | undefined | Array of slice names to persist. If omitted, all slices are persisted | | storage | StorageEngine | localStorage | Storage engine (localStorage, sessionStorage, or custom) | | shouldPersist | (state: S) => boolean | undefined | Function to conditionally persist state (e.g., only when logged in) | | throttle | (fn) => fn | undefined | Function to throttle/debounce save operations. No throttling by default | | serialize | (state) => any | undefined | Transform state before saving (e.g., remove sensitive data, compress) | | deserialize | (state) => any | undefined | Transform state after loading (e.g., decompress, add defaults) | | version | number | undefined | Version number for state migration | | migrate | (state, version) => any | undefined | Function to migrate state between versions | | debug | boolean | false | Enable console logging for debugging |

Returned Methods
saveState(state: S): void

Saves the current Redux state to storage based on configuration.

store.subscribe(() => {
  persist.saveState(store.getState());
});
loadState(): Partial<S> | undefined

Loads and returns the persisted state from storage. Returns undefined if:

  • No state has been persisted
  • State parsing fails
  • Storage access fails
const preloadedState = persist.loadState();
clearState(): void

Removes all persisted state from storage.

// Call this on logout or when clearing user data
persist.clearState();

React Native API (Asynchronous)

createAsyncPersist<S>(config: AsyncPersistConfig<S>)

Creates an async persistence manager for React Native applications using AsyncStorage.

Returns: { saveStateAsync, loadStateAsync, clearStateAsync }

Configuration Options

| Option | Type | Default | Description | | --------------- | ------------------------- | ----------------------- | --------------------------------------------------------------------------- | | key | string | 'persistedReduxState' | Storage key used to store state | | slices | string[] | undefined | Array of slice names to persist. If omitted, all slices are persisted | | storage | AsyncStorageEngine | Required | Async storage engine (use createAsyncStorageEngine(AsyncStorage)) | | shouldPersist | (state: S) => boolean | undefined | Function to conditionally persist state (e.g., only when logged in) | | throttle | (fn) => fn | undefined | Function to throttle/debounce save operations. No throttling by default | | serialize | (state) => any | undefined | Transform state before saving (e.g., remove sensitive data, compress) | | deserialize | (state) => any | undefined | Transform state after loading (e.g., decompress, add defaults) | | version | number | undefined | Version number for state migration | | migrate | (state, version) => any | undefined | Function to migrate state between versions | | debug | boolean | false | Enable console logging for debugging |

Returned Methods
saveStateAsync(state: S): Promise<void>

Asynchronously saves the current Redux state to AsyncStorage.

store.subscribe(() => {
  persist.saveStateAsync(store.getState());
});
loadStateAsync(): Promise<Partial<S> | undefined>

Asynchronously loads and returns the persisted state from AsyncStorage.

const preloadedState = await persist.loadStateAsync();
clearStateAsync(): Promise<void>

Asynchronously removes all persisted state from AsyncStorage.

// Call this on logout or when clearing user data
await persist.clearStateAsync();

createAsyncStorageEngine(asyncStorage)

Creates an AsyncStorage adapter for React Native.

Parameters:

  • asyncStorage - The AsyncStorage instance from @react-native-async-storage/async-storage

Returns: AsyncStorageEngine

import AsyncStorage from "@react-native-async-storage/async-storage";
import { createAsyncStorageEngine } from "redux-selective-persist";

const storage = createAsyncStorageEngine(AsyncStorage);

Shared Utilities

createThrottle(wait: number)

Helper function to create a throttle/debounce function for save operations.

Parameters:

  • wait - Milliseconds to wait before saving

Returns: A throttle function that can be passed to createPersist or createAsyncPersist

import { createPersist, createThrottle } from "redux-selective-persist";

const persist = createPersist({
  throttle: createThrottle(2000), // Debounce saves by 2 seconds
});

createSimplePersist(slices: string[], shouldPersistCheck?: () => boolean)

Legacy API for backward compatibility. Simplified version of createPersist.

Parameters:

  • slices - Array of slice names to persist
  • shouldPersistCheck - Optional function that returns boolean to check if state should persist
const persist = createSimplePersist(
  ["user", "settings"],
  () => localStorage.getItem("isLoggedIn") === "true"
);

Storage Helpers

sessionStorage

Pre-configured storage engine for browser sessionStorage (clears on tab close).

import { createPersist, sessionStorage } from "redux-selective-persist";

const persist = createPersist({
  storage: sessionStorage,
});

createMemoryStorage()

Creates an in-memory storage engine, useful for testing or SSR.

import { createPersist, createMemoryStorage } from "redux-selective-persist";

const memStorage = createMemoryStorage();
const persist = createPersist({
  storage: memStorage,
});

TypeScript Interfaces

StorageEngine (Synchronous)

Interface for custom synchronous storage implementations (Web):

interface StorageEngine {
  getItem(key: string): string | null;
  setItem(key: string, value: string): void;
  removeItem(key: string): void;
}

AsyncStorageEngine (Asynchronous)

Interface for custom async storage implementations (React Native):

interface AsyncStorageEngine {
  getItem(key: string): Promise<string | null>;
  setItem(key: string, value: string): Promise<void>;
  removeItem(key: string): Promise<void>;
}

PersistConfig<S>

Configuration interface for createPersist:

interface PersistConfig<S = any> {
  key?: string;
  slices?: string[];
  storage?: StorageEngine;
  shouldPersist?: (state: S) => boolean;
  throttle?: (fn: (state: S) => void) => (state: S) => void;
  serialize?: (state: any) => any;
  deserialize?: (state: any) => any;
  version?: number;
  migrate?: (persistedState: any, currentVersion: number) => any;
  debug?: boolean;
}

PersistedStateMetadata

Metadata stored alongside your state:

interface PersistedStateMetadata {
  version?: number;
  timestamp: number;
  state: any;
}

TypeScript Support

Full TypeScript support with strong typing and generics:

Basic Type-Safe Usage

import { createPersist } from "redux-selective-persist";

interface UserState {
  id: string;
  name: string;
  email: string;
}

interface SettingsState {
  theme: "light" | "dark";
  language: string;
}

interface RootState {
  user: UserState;
  settings: SettingsState;
  cart: any;
}

// Create type-safe persist
const persist = createPersist<RootState>({
  slices: ["user", "settings"],
});

// Type-safe state loading
const preloadedState: Partial<RootState> | undefined = persist.loadState();

// Configure store with typed state
const store = configureStore({
  reducer: rootReducer,
  preloadedState,
});

Custom Storage with TypeScript

import { StorageEngine } from "redux-selective-persist";

class CustomStorage implements StorageEngine {
  private storage = new Map<string, string>();

  getItem(key: string): string | null {
    return this.storage.get(key) || null;
  }

  setItem(key: string, value: string): void {
    this.storage.set(key, value);
  }

  removeItem(key: string): void {
    this.storage.delete(key);
  }
}

const customStorage = new CustomStorage();
const persist = createPersist<RootState>({
  storage: customStorage,
});

Type-Safe Serialization

const persist = createPersist<RootState>({
  serialize: (state: Partial<RootState>) => {
    // TypeScript knows the shape of state
    return {
      user: state.user
        ? {
            ...state.user,
            // Remove sensitive fields
            email: undefined,
          }
        : undefined,
      settings: state.settings,
    };
  },
  deserialize: (state: any): Partial<RootState> => {
    return {
      user: state.user,
      settings: state.settings,
    };
  },
});

Migration Guide

From redux-persist

If you're migrating from redux-persist, here's a comparison:

redux-persist

import { persistStore, persistReducer } from "redux-persist";
import storage from "redux-persist/lib/storage";

const persistConfig = {
  key: "root",
  storage,
  whitelist: ["user", "settings"],
};

const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = configureStore({ reducer: persistedReducer });
const persistor = persistStore(store);

redux-selective-persist

import { createPersist } from "redux-selective-persist";

const persist = createPersist({
  key: "root",
  slices: ["user", "settings"],
});

const preloadedState = persist.loadState();
const store = configureStore({
  reducer: rootReducer,
  preloadedState,
});

store.subscribe(() => {
  persist.saveState(store.getState());
});

Best Practices

1. Choose Slices Carefully

Only persist what's necessary:

// ✅ Good - Only persist user preferences
const persist = createPersist({
  slices: ["user", "settings", "cart"],
});

// ❌ Avoid - Persisting everything or large datasets
const persist = createPersist(); // Persists entire state

2. Use Conditional Persistence

Prevent persisting when not needed:

const persist = createPersist({
  slices: ["userDrafts", "preferences"],
  shouldPersist: (state) => {
    // Only persist for authenticated users
    return state.auth.isAuthenticated && !state.auth.isGuest;
  },
});

3. Clean Up on Logout

Always clear sensitive data:

const authSlice = createSlice({
  name: "auth",
  initialState,
  reducers: {
    logout: (state) => {
      persist.clearState(); // Clear all persisted state
      // Reset state
      return initialState;
    },
  },
});

4. Use Throttling for Frequently Updated State

const persist = createPersist({
  slices: ["editor", "formData"],
  throttle: createThrottle(1000), // Save max once per second
});

5. Version Your State

Always use versions for production apps:

const persist = createPersist({
  version: 1,
  slices: ["user", "settings"],
  migrate: (state, version) => {
    // Handle future migrations
    return state;
  },
});

6. Remove Sensitive Data

const persist = createPersist({
  slices: ["user"],
  serialize: (state) => ({
    ...state,
    user: {
      ...state.user,
      password: undefined,
      creditCard: undefined,
      ssn: undefined,
    },
  }),
});

7. Handle Errors Gracefully

The library handles errors internally, but you can add extra validation:

const preloadedState = persist.loadState();

// Validate loaded state
if (preloadedState && isValidState(preloadedState)) {
  store = configureStore({
    reducer: rootReducer,
    preloadedState,
  });
} else {
  // Start fresh if state is invalid
  store = configureStore({
    reducer: rootReducer,
  });
}

Common Patterns

Pattern 1: Multi-Environment Setup

const getStorageKey = () => {
  const env = process.env.NODE_ENV;
  const version = process.env.REACT_APP_VERSION;
  return `app_${env}_v${version}`;
};

const persist = createPersist({
  key: getStorageKey(),
  slices: ["user", "settings"],
});

Pattern 2: Feature Flags

const persist = createPersist({
  slices: ["user", "settings"],
  shouldPersist: (state) => {
    // Only persist if feature is enabled
    return state.featureFlags.persistenceEnabled;
  },
});

Pattern 3: A/B Testing

const persist = createPersist({
  key: `app_variant_${state.abTest.variant}`,
  slices: ["user"],
});

Troubleshooting

State Not Persisting

  1. Check if shouldPersist is returning true
  2. Verify slice names match your reducer keys
  3. Enable debug mode to see logs
const persist = createPersist({
  debug: true,
  slices: ["user"],
});

State Not Loading

  1. Check browser console for errors
  2. Verify storage key matches
  3. Check if state structure changed (use migration)

Performance Issues

  1. Add throttling to reduce save frequency
  2. Reduce number of persisted slices
  3. Use serialization to compress data
const persist = createPersist({
  slices: ["essentialData"],
  throttle: createThrottle(2000),
});

Storage Quota Exceeded

  1. Reduce persisted data size
  2. Implement data compression
  3. Clean old data periodically
// Clear old data
if (Date.now() - state.lastClean > 30 * 24 * 60 * 60 * 1000) {
  persist.clearState();
}

Browser Compatibility

Works in all modern browsers that support:

  • ES2020 features
  • localStorage / sessionStorage
  • Map and Set

Supported Browsers

  • Chrome/Edge: ≥ 90
  • Firefox: ≥ 88
  • Safari: ≥ 14
  • Opera: ≥ 76

For older browsers, use Babel transpilation and polyfills.

Framework Integration

React

// store.ts
import { configureStore } from "@reduxjs/toolkit";
import { createPersist } from "redux-selective-persist";
import rootReducer from "./reducers";

const persist = createPersist({
  slices: ["user", "settings"],
});

const preloadedState = persist.loadState();

export const store = configureStore({
  reducer: rootReducer,
  preloadedState,
});

store.subscribe(() => {
  persist.saveState(store.getState());
});

// App.tsx
import { Provider } from "react-redux";
import { store } from "./store";

function App() {
  return <Provider store={store}>{/* Your app */}</Provider>;
}

Next.js (SSR)

import { createPersist, createMemoryStorage } from "redux-selective-persist";

// Check if running in browser
const isBrowser = typeof window !== "undefined";

const persist = createPersist({
  storage: isBrowser ? undefined : createMemoryStorage(),
  slices: ["user", "settings"],
});

Vue

import { createStore } from "vuex";
import { createPersist } from "redux-selective-persist";

const persist = createPersist({
  slices: ["user"],
});

const store = createStore({
  // ...your store config
});

// Load persisted state
const persisted = persist.loadState();
if (persisted) {
  store.replaceState({ ...store.state, ...persisted });
}

// Save on mutations
store.subscribe(() => {
  persist.saveState(store.state);
});

FAQ

Q: How is this different from redux-persist?

redux-selective-persist is simpler, lighter, and more flexible:

  • No need to wrap reducers
  • No persist gates or PersistGate components
  • More straightforward API
  • Smaller bundle size (~2KB vs ~20KB)
  • Better TypeScript support out of the box

Q: Does it work with Redux Toolkit?

Yes! It's designed to work seamlessly with Redux Toolkit:

import { configureStore } from "@reduxjs/toolkit";
import { createPersist } from "redux-selective-persist";

const persist = createPersist({ slices: ["user"] });
const store = configureStore({
  reducer: rootReducer,
  preloadedState: persist.loadState(),
});

Q: Can I use multiple storage engines simultaneously?

Yes! Create multiple persist instances:

const userPersist = createPersist({
  key: "user",
  storage: localStorage,
  slices: ["user"],
});

const tempPersist = createPersist({
  key: "temp",
  storage: sessionStorage,
  slices: ["filters"],
});

Q: How do I handle breaking state changes?

Use the version and migrate options:

const persist = createPersist({
  version: 2,
  migrate: (state, version) => {
    if (version === 2) {
      // Transform old state to new format
      return transformState(state);
    }
    return state;
  },
});

Q: Does it support async operations?

The core library is synchronous, but you can wrap it for async storage:

const asyncPersist = {
  saveState: async (state) => {
    const data = JSON.stringify(state);
    await AsyncStorage.setItem("key", data);
  },
  loadState: async () => {
    const data = await AsyncStorage.getItem("key");
    return data ? JSON.parse(data) : undefined;
  },
};

Q: How do I test components that use persistence?

Use memory storage in tests:

import { createPersist, createMemoryStorage } from "redux-selective-persist";

const testPersist = createPersist({
  storage: createMemoryStorage(),
});

Q: Can I persist only part of a slice?

Yes, use the serialize option:

const persist = createPersist({
  slices: ["user"],
  serialize: (state) => ({
    user: {
      id: state.user.id,
      name: state.user.name,
      // Exclude other user fields
    },
  }),
});

Q: What happens if localStorage is disabled?

The library handles errors gracefully - it will fail silently and not persist data. You can also use a fallback:

const storage = (() => {
  try {
    localStorage.setItem("test", "test");
    localStorage.removeItem("test");
    return undefined; // Use default localStorage
  } catch {
    return createMemoryStorage(); // Fallback to memory
  }
})();

const persist = createPersist({ storage });

Performance Considerations

Benchmarks

  • Save operation: < 1ms for typical state (< 100KB)
  • Load operation: < 1ms for typical state
  • Bundle size: ~2KB minified + gzipped

Optimization Tips

  1. Use selective slices - Only persist what you need
  2. Add throttling - For frequently updated state
  3. Compress large data - Use serialize/deserialize
  4. Use memory storage for tests - Faster than localStorage
// Optimized for performance
const persist = createPersist({
  slices: ["essentialData"], // Minimal data
  throttle: createThrottle(1000), // Reduce save frequency
  serialize: compressData, // Compress before saving
});

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Development Setup

# Clone the repo
git clone https://github.com/Fahim-BAUST/redux-selective-persist.git
cd redux-selective-persist

# Install dependencies
npm install

# Run tests
npm test

# Run tests in watch mode
npm run test:watch

# Build
npm run build

# Lint
npm run lint

Running Tests

# Run all tests
npm test

# Run with coverage
npm run test:coverage

# Watch mode
npm run test:watch

License

MIT © ShiftBoolean

Credits

Inspired by real-world needs for simpler Redux persistence without the complexity of redux-persist.

Links

Support

If you find this package helpful, please consider:

  • ⭐ Starring the repository
  • 🐛 Reporting bugs
  • 💡 Suggesting new features
  • 📝 Improving documentation
  • 🔀 Contributing code

Made with ❤️ by Fahim Ashhab