$ cat ~/writing/til/zustand-persist-hydration.md
Persisted zustand state and hydration mismatches
Reading localStorage during the first client render makes it differ from the server HTML. skipHydration fixes it.
- written
- tags
- react · zustand · nextjs
The accent picker and the sound toggle live in a zustand store persisted to localStorage. The server can't read localStorage, so when the store rehydrated during the first render, the client HTML didn't match the server's.
The pattern that works:
skipHydration: trueon thepersistoptions, so the first render uses defaults like the server did.- Call
useUiStore.persist.rehydrate()once after mount. - An inline script in
<head>applies the saved accent before first paint, so there's no flash of the default colour.