Next.js "Hydration failed because the initial UI does not match"
React hydration requires the client's first render to match the server-rendered HTML exactly. When they diverge - because of Date.now(), window, random values, or invalid nesting - React throws a hydration error.
What this error means
The page logs Hydration failed because the initial UI does not match what was rendered on the server (or Text content does not match server-rendered HTML). It can fail strict CI checks and breaks interactivity on the affected subtree.
Error: Hydration failed because the initial UI does not match what was
rendered on the server.
Warning: Text content did not match. Server: "12:00:00" Client: "12:00:01"
at time
at Clock (Clock.tsx:6:20)Common causes
Non-deterministic render
Using Date.now(), Math.random(), or locale/timezone-dependent formatting produces different output on server and client, so the markup mismatches.
Browser-only APIs during render
Reading window, localStorage, or navigator during the initial render makes the client diverge from the server (where those are undefined).
Invalid HTML nesting
Markup the browser auto-corrects - a <div> inside a <p>, a <p> inside a <p> - makes the parsed client DOM differ from the server string.
How to fix it
Defer client-only values to after mount
Render a stable placeholder on the server, then set the dynamic value in an effect so it only runs on the client.
const [now, setNow] = useState<string | null>(null)
useEffect(() => { setNow(new Date().toLocaleTimeString()) }, [])
return <time>{now ?? ''}</time>Fix invalid nesting and guard browser APIs
- Validate HTML nesting (no block elements inside
<p>, no<div>inside inline elements). - Never read
window/localStorageduring render; do it in an effect. - For inherently client-only widgets, load them with
next/dynamicand{ ssr: false }.
How to prevent it
- Keep the initial render deterministic and server-safe.
- Move browser-API reads into effects, not render.
- Validate HTML nesting so the parsed DOM matches the server string.