Skip to content
Persist
Esc
↑↓navigate↵open⌘Jpreview
On this page

Migrating from …

Map zustand-persist, redux-persist, and pinia-persist options onto Persist seams.

Same mental model everywhere: pick a source (PersistableSource), wire storage, gate UI with HydrationSignal. Option names map 1:1 where noted; gaps are explicit.

Migrating from zustand-persist

Near 1:1 — this API is modeled on zustand persist, plus store-agnostic sources, a first-class HydrationSignal, and a codec seam.

zustand-persist @stainless-code/persist
name name
storage / createJSONStorage storage / createJSONStorage (core)
partialize partialize
version version
migrate migrate
merge merge
onRehydrateStorage onRehydrateStorage
skipHydration skipHydration
persist middleware persistStore(store, opts) or persistSource(source, opts)
— toHydrationSignal(persist) + useHydrated (no zustand equivalent)
— crossTab, maxAge, buster, throttleMs, retryWrite (no zustand equivalent)
// zustand-persist
import { create } from "zustand";
import { persist, createJSONStorage } from "zustand/middleware";

export const usePrefs = create(
  persist(() => ({ theme: "light" }), {
    name: "app:prefs:v1",
    storage: createJSONStorage(() => localStorage),
  }),
);

// @stainless-code/persist — same zustand store, adapter instead of middleware
import { create } from "zustand";
import { createJSONStorage, toHydrationSignal } from "@stainless-code/persist";
import { persistStore } from "@stainless-code/persist/sources/zustand";

export const usePrefs = create(() => ({ theme: "light" }));
const persist = persistStore(usePrefs, {
  name: "app:prefs:v1",
  storage: createJSONStorage(() => localStorage),
});
export const prefsHydration = toHydrationSignal(persist);

Migrating from redux-persist

redux-persist owns writes inside persistReducer. Here wrap the root reducer with persistableReducer (not each slice) and call persistStore. Redux has no setState; RTK slices ignore foreign hydrate actions otherwise. Alias if you still import redux-persist’s persistStore.

redux-persist @stainless-code/persist
key name
storage storage
version version
migrate migrate
whitelist / blacklist partialize (project the slice)
transforms merge (per-reducer in/out)
serialize / deserialize custom StorageCodec via createStorage
stateReconciler merge (default shallow-spread; customize)
throttle throttleMs
persistReducer / persistStore persistableReducer + persistStore from ./sources/redux
— toHydrationSignal + framework adapter (no redux-persist equivalent)
— crossTab, maxAge, buster, retryWrite, schema validation (no eq.)
// redux-persist
import { createStore } from "redux";
import { persistReducer, persistStore } from "redux-persist";
import storage from "redux-persist/lib/storage";

const persistedReducer = persistReducer({ key: "root", storage }, rootReducer);
export const store = createStore(persistedReducer);
persistStore(store);

// @stainless-code/persist
import { createStore } from "redux";
import { createJSONStorage, toHydrationSignal } from "@stainless-code/persist";
import {
  persistStore,
  persistableReducer,
} from "@stainless-code/persist/sources/redux";

export const store = createStore(persistableReducer(rootReducer));
const persist = persistStore(store, {
  name: "root",
  storage: createJSONStorage(() => localStorage),
});
export const rootHydration = toHydrationSignal(persist);

Migrating from pinia-persist

pinia-persist is a Pinia plugin; here call persistStore on the store — same storage, partialize, optional codec.

pinia-persist @stainless-code/persist
key name
storage storage
paths partialize (pick paths)
serializer custom StorageCodec or default jsonCodec via createStorage
beforeHydrate / afterHydrate onRehydrateStorage
debug onError
pinia.use(plugin) persistStore(store, opts) from ./sources/pinia
— toHydrationSignal + framework adapter (no equivalent)
— crossTab, migrate, maxAge, buster, throttleMs, retryWrite (no equivalent)
// pinia-persist
import { createPinia, defineStore } from "pinia";
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";

const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
export const usePrefsStore = defineStore("prefs", {
  state: () => ({ theme: "light", draft: "" }),
  persist: { key: "prefs", paths: ["theme"] },
});

// @stainless-code/persist
import { defineStore } from "pinia";
import { createJSONStorage, toHydrationSignal } from "@stainless-code/persist";
import { persistStore } from "@stainless-code/persist/sources/pinia";

export const usePrefsStore = defineStore("prefs", {
  state: () => ({ theme: "light", draft: "" }),
});
const prefsStore = usePrefsStore();
const persist = persistStore(prefsStore, {
  name: "prefs",
  storage: createJSONStorage(() => localStorage),
  partialize: (s) => ({ theme: s.theme }),
});
export const prefsHydration = toHydrationSignal(persist);

Last updated on

Was this page helpful?