install
source · Clone the upstream repo
git clone https://github.com/Intense-Visions/harness-engineering
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/claude-code/redux-persistence-pattern" ~/.claude/skills/intense-visions-harness-engineering-redux-persistence-pattern-bd160b && rm -rf "$T"
manifest:
agents/skills/claude-code/redux-persistence-pattern/SKILL.mdsource content
Redux Persistence Pattern
Persist and rehydrate Redux state across browser sessions with redux-persist or manual localStorage strategies
When to Use
- Preserving user preferences, cart items, or draft content across page reloads
- Implementing offline-first features that need state survival
- Selectively persisting some slices while keeping others ephemeral
- Migrating persisted state when the schema changes between app versions
Instructions
- Choose between
(full library) or manual persistence (listener middleware + localStorage). Useredux-persist
for complex needs (transforms, migrations, multiple storage engines). Use manual for simple cases.redux-persist - With redux-persist: Wrap the root reducer with
, create apersistReducer
, and wrap the app withpersistor
.PersistGate - Use a
orwhitelist
to control which slices are persisted. Default to whitelist (explicit opt-in).blacklist - Add
,FLUSH
,REHYDRATE
,PAUSE
,PERSIST
,PURGE
to the serializable check ignore list — these are internal redux-persist actions.REGISTER - Use
when the persisted state schema changes between versions.createMigrate - Manual approach: Use the listener middleware to write specific state to localStorage on change, and
inpreloadedState
to rehydrate on startup.configureStore
// redux-persist approach 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 { combineReducers } from '@reduxjs/toolkit'; import todosReducer from './features/todos/todos.slice'; import uiReducer from './features/ui/ui.slice'; const persistConfig = { key: 'root', version: 1, storage, whitelist: ['todos'], // Only persist todos, not UI state }; const rootReducer = combineReducers({ todos: todosReducer, ui: uiReducer, }); 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);
// App entry import { PersistGate } from 'redux-persist/integration/react'; import { persistor, store } from './store'; function App() { return ( <Provider store={store}> <PersistGate loading={<Spinner />} persistor={persistor}> <Root /> </PersistGate> </Provider> ); }
Details
Manual persistence (simpler, no extra dependency):
// Rehydrate on startup const preloadedTodos = JSON.parse(localStorage.getItem('todos') ?? 'null'); const store = configureStore({ reducer: { todos: todosReducer }, preloadedState: preloadedTodos ? { todos: preloadedTodos } : undefined, }); // Persist on change via listener middleware listenerMiddleware.startListening({ predicate: (action, currentState, previousState) => currentState.todos !== previousState.todos, effect: (action, listenerApi) => { localStorage.setItem('todos', JSON.stringify((listenerApi.getState() as RootState).todos)); }, });
State migrations: When adding or renaming fields between app versions:
const migrations = { 1: (state: any) => ({ ...state, newField: 'default' }), 2: (state: any) => { const { removedField, ...rest } = state; return rest; }, }; const persistConfig = { key: 'root', version: 2, storage, migrate: createMigrate(migrations), };
Storage engines:
redux-persist/lib/storage uses localStorage. For React Native, use @react-native-async-storage/async-storage. For session-only persistence, use redux-persist/lib/storage/session.
What NOT to persist: Loading states, error messages, ephemeral UI state (modals, tooltips), data that should be fresh from the server.
Source
https://github.com/rt2zz/redux-persist
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.