Hydration Mismatch in SSR Component
A React component renders different markup on the server than on the client, typically because it reads time, randomness, or browser APIs during initial render.
Typical error
Hydration failed because the initial UI does not match what was rendered on the server
What this is
Next.js renders your page HTML on the server, then hydrates (attaches event handlers) on the client. If the first client render produces different markup, React logs a hydration error. The page may flash the server HTML and then re-render from scratch, destroying Core Web Vitals.
Common causes:
Date.now(),new Date(), orMath.random()used during render- Reading
localStorage,sessionStorage,window, ornavigatorat render time - Third-party scripts injecting markup before React hydrates
- Conditional rendering based on
typeof window !== 'undefined'
Why AI tools ship this
AI-generated components often reach for Date.now() for keys, timestamps, or "random id" patterns without realizing those change between server and client.
How to detect
Watch the browser console on first load. "Hydration failed" and "Text content does not match server-rendered HTML" are the canonical messages.
For static detection, grep:
grep -rE "(Math.random|Date.now|new Date\(\))" --include="*.tsx" app componentsAny occurrence in a non-event-handler position is suspect.
How to fix
Wrap anything that must only run on the client:
'use client'
import { useEffect, useState } from 'react'
export function ClientOnly({ children }: { children: React.ReactNode }) {
const [mounted, setMounted] = useState(false)
useEffect(() => setMounted(true), [])
if (!mounted) return null
return <>{children}</>
}Or move the non-deterministic value out of render into a useEffect:
const [now, setNow] = useState<number | null>(null)
useEffect(() => setNow(Date.now()), [])For ids, use React's useId() hook instead of random values.
Related
- Glossary: hydration mismatch
- Glossary: error boundary