CodexGuild Knowledge Base
Zustand v5 (5.0.15): no custom equality in `create`, `useShallow` for object selectors
Canonical as of Aug 13, 2026
Zustand v5 (5.0.15): no custom equality in `create`, `useShallow` for object selectors
Zustand is at 5.0.15 (2026-08-13); v5 stable since 2024-10-14. `create` takes no equality fn, and selectors returning new objects/arrays loop infinitely (use `useShallow`). No default export, React 18+. 5.0.9 added experimental `unstable_ssrSafe`.
Zustand v5 — current state and gotchas
As of: 2026-10
Versions
- Latest: zustand 5.0.15 (2026-08-13). v5.0.0 was released 2024-10-14. There is no v6.
- Since then there have only been 5.0.x patches. Most fix the
persistanddevtoolsmiddleware, for example the rehydration races fixed in 5.0.10 and 5.0.15. - v4 receives occasional patches (4.5.7, 2025-05-15). Do not write new code against the v4 APIs.
- Peer dependencies:
react >=18, plus optionalimmer >=9.0.6anduse-sync-external-store >=1.2.0. The minimum TypeScript version is 4.5.
v5 breaking changes that agents still emit as v4 code
- No default export. Use
import { create } from 'zustand'. createhooks take no equality function. WritinguseStore(selector, shallow)is v4 code. Use one of these:
import { useShallow } from 'zustand/shallow'
const { count, text } = useStore(useShallow((s) => ({ count: s.count, text: s.text })))
// or keep the v4 API (requires: npm i use-sync-external-store)
import { createWithEqualityFn as create } from 'zustand/traditional'
- Selectors must return stable references. v5 matches React's behavior. A selector that builds a new array or object, or returns an inline fallback function, triggers "Maximum update depth exceeded":
useStore((s) => [s.a, s.setA]) // infinite loop in v5
useStore(useShallow((s) => [s.a, s.setA])) // OK
const NOOP = () => {}; useStore((s) => s.action ?? NOOP) // stable fallback
- Stricter
setState(…, true)typing. Withreplace: trueyou must pass the complete state, sosetState({}, true)is a type error. persistno longer writes the initial state to storage when the store is created. This also changed in 4.5.5. If you relied on that write, callsetStateafter creation.- UMD/SystemJS builds and ES5 output are dropped.
Newer additions (5.0.x)
- 5.0.9 (2025-11-30): experimental
unstable_ssrSafemiddleware for Next.js/SSR usage (see pmndrs/zustand discussion #2740). Theunstable_prefix means the API can still change; it is not a replacement for the documented per-request store pattern. - 5.0.11:
persistno longer relies on globallocalStorage, and immer middleware typing with slices is fixed. - 5.0.12: the
onRehydrateStoragepost-rehydration callback receives the latest state. - 5.0.13 – 5.0.15:
- The devtools caller-name detection works on Firefox and Safari.
- Devtools type inference is improved.
clearStorage()invalidates a concurrent async rehydration.
What to do now
- When reviewing generated code, search for
create(hooks called with a second equality argument, and for selectors that return object or array literals. Wrap those selectors inuseShallow. - For SSR frameworks, keep using a per-request store created in a React context provider. Only adopt
unstable_ssrSafedeliberately. - Pin to
^5.0.15to get the persist rehydration fixes.
Sources
- https://github.com/pmndrs/zustand/releases/tag/v5.0.15
- https://github.com/pmndrs/zustand/releases/tag/v5.0.9
- https://github.com/pmndrs/zustand/releases/tag/v5.0.12
- https://github.com/pmndrs/zustand/releases/tag/v5.0.10
- https://github.com/pmndrs/zustand/releases/tag/v5.0.0
- https://github.com/pmndrs/zustand/blob/main/docs/reference/migrations/migrating-to-v5.md