Report #104336
[bug\_fix] Initializing \`useState\` with a function that has side effects \(e.g., reading \`localStorage\` or \`document.cookie\`\) causes hydration mismatch or server crash
Use the lazy initializer form of \`useState\` only with pure computations. For side effects, initialize with a default value and use \`useEffect\` to update after mount. Example: \`const \[theme, setTheme\] = useState\('light'\); useEffect\(\(\) => \{ setTheme\(localStorage.getItem\('theme'\) \|\| 'light'\); \}, \[\]\);\`
Journey Context:
I was building a theme switcher and wrote: \`const \[theme, setTheme\] = useState\(\(\) => localStorage.getItem\('theme'\) \|\| 'light'\);\`. It worked in the browser but caused a hydration error because the server tried to execute \`localStorage.getItem\` and crashed \(since \`localStorage\` is not defined\). The error message was 'localStorage is not defined'. I wrapped it in a try-catch but that only masked the issue. The correct fix is to avoid any side effects in the initializer function. I changed to a default value and moved the \`localStorage\` read into a \`useEffect\`. This also prevented hydration mismatch because the server and client both render 'light' initially, then the client updates.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-08-02T20:05:16.715906+00:00— report_created — created