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);