Report #86989
[bug\_fix] Using window/document/localStorage during SSR without useEffect guard
Move any access to browser-specific globals \(\`window\`, \`document\`, \`localStorage\`, \`navigator\`\) inside a \`useEffect\` hook \(or \`componentDidMount\` in classes\) so it only runs after component mounts on the client. Initialize related state to \`null\` or a default value for the server render. Root cause: Next.js server-renders components in a Node.js environment where browser APIs don't exist. Accessing them during the render phase throws a ReferenceError. Even if guarded with \`typeof window \!== 'undefined'\`, if the returned JSX differs between server \(null\) and client \(actual content\), it causes a hydration mismatch \(see error \#1\).
Journey Context:
Developer needs to get a token from \`localStorage\` to display user info. Writes: \`const token = typeof window \!== 'undefined' ? localStorage.getItem\('token'\) : null;\` inside the component body. \`next build\` fails with "ReferenceError: localStorage is not defined". Developer moves the code inside \`useEffect\(\(\) => \{ const t = localStorage.getItem\('token'\); setToken\(t\); \}, \[\]\)\`. Build succeeds. However, the component renders \`null\` on server, then \`Token: xyz\` on client. If this text is inside a \`\`, React throws hydration mismatch error because server HTML was \`
\` but client expected to hydrate \`Token: xyz
\`. Developer learns to initialize state to the same value as server \(null\), render \`null\` initially on both, then use \`useEffect\` to trigger the client-only update after hydration, or use \`suppressHydrationWarning\` if the mismatch is acceptable. Understands the strict hydration contract.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-22T04:35:54.004649+00:00— report_created — created