Engrain by ByteRush

RCT.06.02 Mid-level· 11 minCore — asked often

Hydration and its errors: when server HTML and the client disagree

Hydration attaches React to server-rendered HTML. Learn what causes mismatches (dates, random values, window checks, browser extensions), the right fixes, when suppressHydrationWarning is allowed, and why hydration cost leads to islands.

Comes up in: React, Debugging

Learn#

Megha’s server-rendered page flashes and logs “Hydration failed” on some users’ machines. On those machines the header’s date shows yesterday, then jumps to today.

Hydration is how React takes over server HTML: it renders the same components in the browser, matches the result to the existing DOM, and attaches event handlers instead of recreating everything. That only works if the first client render produces exactly the same output:

import { GlobalRegistrator } from "@happy-dom/global-registrator";
GlobalRegistrator.register(); // a DOM for Node; in a browser you already have one
globalThis.IS_REACT_ACT_ENVIRONMENT = true;
const { createElement: h, useEffect, useState, act } = await import("react");
const { renderToString } = await import("react-dom/server");
const { hydrateRoot } = await import("react-dom/client");

let isServer = true;
function BadClock() { // different output on server and client: a mismatch
  return h("p", null, isServer ? "Rendered on the server" : "Rendered in the browser");
}
function GoodClock() { // same first render everywhere, then update after hydration
  const [where, setWhere] = useState("Loading…");
  useEffect(() => setWhere("Rendered in the browser"), []);
  return h("p", null, where);
}
for (const Clock of [BadClock, GoodClock]) {
  isServer = true;
  const box = document.createElement("div");
  box.innerHTML = renderToString(h(Clock)); // the HTML the server sent
  isServer = false;
  const problems = [];
  await act(() => hydrateRoot(box, h(Clock), { onRecoverableError: (e) => problems.push(e.message.split(".")[0]) }));
  console.log(Clock.name, "→", box.textContent, "|", problems.length ? problems[0] : "no hydration error");
}
This example uses Node.js, so it runs in our checks rather than in your browser.

Output (verified)

BadClock → Rendered in the browser | Hydration failed because the server rendered text didn't match the client
GoodClock → Rendered in the browser | no hydration error

A hydration mismatch comes from anything that differs between server and browser: dates and time zones, Math.random(), typeof window checks, localStorage, invalid HTML nesting, and browser extensions that edit the page.

A mismatch, and the safe fixStep 1 of 4

Page

  • <p>Rendered on the server</p>

State

    empty

    Console

      empty

      The server renders "Rendered on the server" and sends it as HTML. Users see it before any JavaScript runs.

      Read the steps as text
      1. The server renders "Rendered on the server" and sends it as HTML. Users see it before any JavaScript runs.
      2. The bundle loads. hydrateRoot renders the same component in the browser and expects identical output.
      3. The text differs. React reports a hydration error and re-renders that part on the client, throwing away the server HTML there.
      4. The fix: render the same first output everywhere ("Loading…"), then update in an effect after hydration.

      Predict#

      Predict first

      A component renders typeof window !== 'undefined' ? <MobileMenu/> : <DesktopMenu/>. What happens with SSR?

      Interview angle#

      Say it like this — about 30 seconds

      “Hydration makes server-rendered HTML interactive: React renders the same tree in the browser, reuses the existing DOM nodes and attaches event handlers. The first client render must match the server exactly. Mismatches come from dates and time zones, random ids, window or localStorage checks, invalid HTML nesting and browser extensions. I fix them by rendering the same output first and updating in an effect, or using useId for ids. suppressHydrationWarning is only for unavoidable text, such as a timestamp.”

      Stays on your device. Nothing is uploaded.
      Follow-up What is hydration? What causes hydration mismatches?
      Attaching React to server HTML by re-rendering and matching it. Mismatches: anything that renders differently on the server and client.
      Follow-up When is suppressHydrationWarning acceptable?
      Only on an element whose text cannot match, such as a formatted current time; it works one level deep and only hides the warning.
      Follow-up How does invalid HTML cause mismatches?
      The browser’s parser fixes bad nesting, such as a div inside a p, so the DOM differs from what React expects.

      Practice#

      Guided

      Write a small version of React’s hydration check: find where two rendered trees first differ.

      Done when

      • Tag and text differences are reported at that node’s path.
      • A different number of children is reported at the parent.
      • Identical trees return null.
      Your solution
      Ctrl/⌘ + Enter

      Remember#

      Try to answer each card before you open it. Then add them to your reviews.

      What is hydration?

      React attaching to server HTML by rendering in the browser and reusing the DOM.

      Name three causes of hydration mismatches.

      Dates or time zones, Math.random, typeof window checks (also extensions, invalid HTML).

      How do you show browser-only content safely?

      Render the same placeholder first, then update in an effect.

      What does suppressHydrationWarning do?

      Hides text mismatch warnings for one element; it does not fix them.

      Go deeper

      Four layers. Stop at the one your interview needs; each ends with a quick check.

      The core ideaEvery round

      Render the same output first, then change it in an effect; use useId for ids; format dates with a fixed time zone on both sides, or show them only after hydration.
      On a mismatch, React re-renders the nearest Suspense boundary on the client, which costs time and can flash.

      Check yourself: What happens to the server HTML in a part that mismatches?

      React discards it and renders that part on the client.

      How it works underneathSenior rounds

      hydrateRoot walks the existing DOM while rendering and reuses each node that matches Source: React: hydrateRoot. In React 19, mismatch errors include a diff of the differing text.

      Check yourself: Which API starts hydration?

      hydrateRoot(container, element).

      Edge cases and trapsWhere people slip

      Extensions such as translators and password managers add attributes or nodes before React hydrates. React 19 skips many unexpected third-party additions in <head> and <body>, but content inside your app can still be affected.

      Check yourself: Why might only some users see hydration errors?

      Browser extensions or time zones change the HTML or the output on their machines.

      In productionStaff-level answers
      Hydration costs CPU time proportional to the whole page’s components. Islands (Astro) hydrate only interactive parts, and Server Components (RCT.06.03) send no JavaScript for static parts Source: Astro: Islands architecture.
      Check yourself: What is islands architecture?

      Static HTML pages with small, separately hydrated interactive parts.

      Finished the five steps? Mark it, and its cards join your reviews.

      Last checked 2026-11-01. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.

      Interview questions this answers

      • What is hydration? What causes hydration mismatches?