Hydration failed because the initial UI does not match what was rendered on the server
Any AI coding tool Last checked 2026-10-29
This React/Next.js error means the HTML the server sent and the HTML the browser rendered on first paint disagree. It almost always traces to code the agent wrote that reads window, Date.now(), Math.random(), or browser-only storage during render, or renders different markup on server vs. client.
What you saw
The browser console shows: Error: Hydration failed because the initial UI does not match what was rendered on the server.
Sometimes followed by a diff block showing + and - lines of mismatched HTML, or the shorter Text content does not match server-rendered HTML variant.
The page often still works after the mismatch — React throws the warning, discards the server HTML, and re-renders client-side — but layout flickers and anything stateful on first load can desync.
What it actually means
React renders your component twice: once on the server (or at build time) to produce HTML, and once in the browser to attach event handlers. Hydration is the step where React compares the two and reuses the server HTML if they match.
An agent adding a feature commonly introduces one of these without realizing server and client environments differ:
- Browser-only globals read during render.
window, document, localStorage, or navigator do not exist on the server. If a component reads one directly in its render body (not inside useEffect), the server renders a fallback value and the client renders the real one. - Non-deterministic values.
Date.now(), Math.random(), or new Date().toLocaleString() produce a different value on each render — the server's timestamp differs from the client's by however many milliseconds the request took. - Conditional rendering based on
typeof window. This is the classic anti-pattern: it looks like a guard, but it deliberately makes server and client render different trees. - Invalid HTML nesting. The browser silently fixes invalid nesting (like a
<div> inside a <p>) while parsing, producing a DOM tree that doesn't match what React expects from its own server-rendered string. - Third-party scripts or browser extensions (Grammarly, dark-mode togglers) injecting attributes into the DOM before React hydrates — this one is not your code, and the fix is different.
The fix
Read the diff React prints — it names the exact element and attribute that mismatched. Fix that one thing before changing anything else; this error cascades and the first line is almost always the real cause.
Move any browser-only read out of the render body and into an effect, then store the result in state so the first render matches on both sides:
const [width, setWidth] = useState(0);
useEffect(() => {
setWidth(window.innerWidth);
}, []);
For values that must differ by design (a client-only widget, a random ID), suppress the one-time mismatch explicitly instead of letting it warn — this tells React the difference is intentional:
<span suppressHydrationWarning>{clientOnlyValue}</span>
For Date/Math.random() used in rendering, compute the value once in useEffect or defer rendering the dynamic part until after mount:
const [mounted, setMounted] = useState(false);
useEffect(() => setMounted(true), []);
if (!mounted) return null; // or a static placeholder
If the cause is invalid HTML nesting, check the DOM the browser actually built in DevTools against your JSX — look for a block element nested inside an inline one, or a <table> without <tbody>.
Rule out a browser extension before touching code: open the page in a private window with extensions disabled. If the error disappears, it is Grammarly/dark-mode/password-manager DOM injection, not your app.
How to stop it happening again
Tell the agent explicitly: never read window, document, localStorage, Date.now(), or Math.random() directly inside a component's render body — only inside useEffect, an event handler, or behind a mounted check.
Add a project rule that any new use of browser-only APIs during render requires a hydration-safe pattern (lazy state, useEffect, or suppressHydrationWarning with a one-line comment explaining why).
Run the app once in a private browser window with extensions off before concluding a hydration error is a code bug.
Prefer passing server-computed values down as props over recomputing them differently on the client.
Related questions
Is this error dangerous — can I ignore it?
Not safely. React discards the server-rendered DOM and rebuilds it client-side, which causes a visible flash and can desync state tied to the mismatched element (scroll position, focus, form values). Fix it rather than suppress it broadly.
Why does it only happen in production, not next dev?
Dev mode is more forgiving about timing and sometimes masks the mismatch, or you simply haven't hit the code path. Run next build && next start locally to reproduce production hydration behavior before shipping.
The agent wrapped everything in suppressHydrationWarning to make the error go away — is that okay?
Only for the one element whose difference is intentional and understood. Using it broadly hides real bugs — the server and client HTML genuinely diverge, and suppressHydrationWarning just stops React from telling you.