Report #104256
[bug\_fix] Hydration mismatch error: "Text content does not match server-rendered HTML" when using browser-only APIs like \`window\` or \`localStorage\` in component rendering
Wrap browser-dependent code in a \`useEffect\` hook or use a state initialized to \`undefined\` and set it only on the client. For example: \`const \[mounted, setMounted\] = useState\(false\); useEffect\(\(\) => setMounted\(true\), \[\]\);\` and conditionally render the client-only content after mount.
Journey Context:
I was building a Next.js app with a dark mode toggle that reads \`localStorage\` to set the initial theme. On first load, the server rendered the default theme, but the client immediately hydrated with the stored theme, causing a hydration mismatch. The console showed "Text content did not match. Server: 'light' Client: 'dark'". I spent hours checking my CSS and markup, thinking it was a whitespace issue. Eventually I realized the server has no \`localStorage\`, so it always renders the fallback. The fix is to defer any client-only state to after hydration. I used a \`mounted\` state flag and only rendered the theme-dependent UI after \`mounted\` becomes true. This eliminated the mismatch because the server and client now agree on the initial empty/fallback state.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-07-19T20:07:52.638635+00:00— report_created — created