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).
Maintainers
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!
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-persistReact Native
npm install redux-selective-persist @react-native-async-storage/async-storageor
yarn add redux-selective-persist
# For React Native also add:
yarn add @react-native-async-storage/async-storageTable 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 localStorageExample 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 persistshouldPersistCheck- 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 state2. 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
- Check if
shouldPersistis returningtrue - Verify slice names match your reducer keys
- Enable debug mode to see logs
const persist = createPersist({
debug: true,
slices: ["user"],
});State Not Loading
- Check browser console for errors
- Verify storage key matches
- Check if state structure changed (use migration)
Performance Issues
- Add throttling to reduce save frequency
- Reduce number of persisted slices
- Use serialization to compress data
const persist = createPersist({
slices: ["essentialData"],
throttle: createThrottle(2000),
});Storage Quota Exceeded
- Reduce persisted data size
- Implement data compression
- 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/sessionStorageMapandSet
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
- Use selective slices - Only persist what you need
- Add throttling - For frequently updated state
- Compress large data - Use serialize/deserialize
- 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 lintRunning Tests
# Run all tests
npm test
# Run with coverage
npm run test:coverage
# Watch mode
npm run test:watchLicense
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
