Lesson 19 / 25
persist, devtools and immer Middleware
Wrap the state creator.
Middleware wraps the creator function
Zustand middleware are functions that wrap the state creator. persist (from zustand/middleware) saves state to storage under a name key, with options such as storage: createJSONStorage(() => sessionStorage), partialize to choose fields, and version plus migrate for schema changes. devtools connects the store to the Redux DevTools extension. immer (from zustand/middleware/immer, requires the immer package) lets actions mutate a draft. When combining them, the docs recommend applying devtools last, as the outermost wrapper.
Compose, type and test
Middleware, vanilla stores and test resets make Zustand production-ready.
A persisted settings store
devtools(persist(immer(...))).
import { create } from 'zustand';
import { devtools, persist, createJSONStorage } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
type Settings = {
theme: 'light' | 'dark';
recent: string[];
setTheme: (t: Settings['theme']) => void;
remember: (q: string) => void;
};
export const useSettings = create<Settings>()(
devtools(
persist(
immer((set) => ({
theme: 'light',
recent: [],
setTheme: (t) => set((s) => { s.theme = t; }),
remember: (q) => set((s) => { s.recent = [q, ...s.recent].slice(0, 5); }),
})),
{
name: 'settings',
storage: createJSONStorage(() => localStorage),
partialize: (s) => ({ theme: s.theme, recent: s.recent }),
version: 1,
}
),
{ name: 'SettingsStore' }
)
);Persist only what you must
Persisted data outlives deployments. Use partialize to save only stable preferences, bump version with a migrate function when the shape changes, and never persist secrets in localStorage.
Quick check: What does the persist option partialize do?
- Encrypts state
- Splits the store into slices
- Compresses state
- Chooses which parts of state are saved to storage
Answer
Chooses which parts of state are saved to storage — Only the returned fields are persisted.