Overview

Hydration is the client step that makes server-rendered HTML interactive: the framework re-runs the component tree in the browser, checks it against the existing DOM, reuses the nodes, and attaches handlers. A page that looks ready but ignores clicks on slow phones is waiting on hydration. Ship less client JavaScript or hydrate only interactive parts. A page with no interactivity needs no hydration and no JavaScript.

Rules

  • Shrink the work: Astro islands (client:* directives) hydrate only interactive components; server-component output never hydrates, only Client Components do; Suspense boundaries hydrate independently (suspense-boundary); Qwik skips hydration by resuming server state on first interaction (resumability).
  • Make the first client render match the server HTML. Mismatches come from window or localStorage checks during render, Date.now(), Math.random(), locale or timezone differences, client-only auth state, invalid HTML nesting, and browser extensions that alter the DOM.
  • React may recover by client-rendering the affected part, but it does not guarantee attribute differences are patched and handlers can attach to the wrong elements. Treat every mismatch as a bug.
  • Read browser-only values in useEffect after mount (a second render), use useId for IDs, and use suppressHydrationWarning only for unavoidable text such as timestamps (one level deep).
function Clock() {
  const [time, setTime] = useState("");           // same on server and first client render
  useEffect(() => setTime(new Date().toLocaleTimeString()), []);
  return <time>{time}</time>;
}