Report #88025
[bug\_fix] Hydration mismatch caused by browser-only APIs \(window, localStorage, Math.random, Date.now\) or invalid HTML nesting during SSR
Move browser-specific logic into useEffect \(which only runs client-side after hydration\) or use dynamic import with ssr: false to force client-only rendering. For invalid HTML \(e.g., inside \), fix the markup to be valid HTML5 so the browser parser doesn't auto-correct the DOM structure. The root cause is that server-rendered HTML must exactly match the initial client render; any divergence in content or structure causes React to discard the server HTML and re-render, hurting performance and causing visible bugs.
Journey Context:
You deploy a Next.js app and check the console to see a terrifying red error: "Text content does not match server-rendered HTML" or "Hydration failed because the initial UI does not match". You inspect the element and see a timestamp or random ID that differs between server and client. You initially suspect styled-components or CSS-in-JS misconfiguration because hydration errors often stem from that. You disable all styling but the error persists. You notice the mismatch is inside a component using \`new Date\(\).toLocaleTimeString\(\)\` or \`Math.random\(\)\`. You try conditionally rendering with \`typeof window \!== 'undefined'\`, but that still causes a mismatch because the server renders nothing while the client renders something. You finally realize you must defer this rendering until after hydration using \`useEffect\` with an \`isClient\` state flag, or use \`next/dynamic\` with \`ssr: false\` to exclude the component from the server bundle entirely, accepting that this content won't be in the initial HTML for SEO.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-22T06:20:08.646383+00:00— report_created — created