Back to Docs
Recipe
Zustand Patterns
Production-grade state recipes for Meridian loaders, auth flows, and real-time dashboards.
Slice Factory
Compose independent slices into a single store. Each slice owns its state, actions, and selectors — no cross-slice imports.
const createAuthSlice = (set) => ({
session: null,
login: (s) => set({ session: s }),
})
const createLoaderSlice = (set) => ({
status: 'idle',
inject: () => set({ status: 'injecting' }),
})
export const useStore = create((...a) => ({
...createAuthSlice(...a),
...createLoaderSlice(...a),
}))Selector Memoization
Use atomic selectors to prevent re-renders. Zustand diffs by reference — return primitives or stable references only.
// Good: primitive selector
const status = useStore((s) => s.status)
// Good: derived with shallow
import { shallow } from 'zustand/shallow'
const [user, token] = useStore(
(s) => [s.user, s.token],
shallow
)Middleware Stack
Layer persist, devtools, and immer in the correct order. Persist wraps the entire store — place it outermost.
import { persist, devtools } from 'zustand/middleware'
import { immer } from 'zustand/middleware/immer'
export const useStore = create(
devtools(
persist(
immer((set) => ({
counters: {},
inc: (id) =>
set((s) => {
s.counters[id] = (s.counters[id] || 0) + 1
}),
})),
{ name: 'meridian-counters' }
),
{ name: 'MeridianStore' }
)
)External Sync
Subscribe outside React for WebSocket or IPC updates. UseuseStore.getStateanduseStore.setStatewithout triggering component trees.
// In your WebSocket handler (outside React)
ws.onmessage = (ev) => {
const data = JSON.parse(ev.data)
useStore.setState({ heartbeat: data.ts })
}
// Read state imperatively
const token = useStore.getState().session?.tokenThese patterns power the Meridian loader dashboard, license validator, and real-time injection monitor. See the License Flow recipe for end-to-end integration.