$ cd ..
$ 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:

  1. skipHydration: true on the persist options, so the first render uses defaults like the server did.
  2. Call useUiStore.persist.rehydrate() once after mount.
  3. An inline script in <head> applies the saved accent before first paint, so there's no flash of the default colour.
-- INSERT --