Hydration Failed in React: Causes and Fixes

Fix the React hydration error by making the server and client render the same markup, and reserve two-pass rendering and suppression for unavoidable cases.

7 min read

React hydration fails when the HTML the server generated does not match what the client renders on its first pass. The error means React cannot safely attach to the page it was given, because the markup diverged somewhere. Find and fix that divergence and the error disappears.

Why hydration happens

Hydration attaches React to HTML that the server already rendered. React walks the existing DOM and matches each element to the component tree, wiring up event handlers and state without rebuilding the page.

If the text or an attribute differs, React reports "Hydration failed because the server rendered HTML didn't match the client." The mismatch is the bug, not a symptom to ignore. React treats mismatches as problems to fix, and there is no guarantee it will patch attribute differences.

The common causes

Most hydration failures come from a handful of render-time decisions that produce different output on the server and in the browser.

CauseWhy it diverges
Extra whitespace around the rootthe raw HTML differs from the component output
typeof window checks in renderthe server and client take different branches
Browser-only APIs in renderwindow.matchMedia exists only in the browser
Different data on each sidetimestamps and random values differ per run

Match the server render

A component that reads the clock during render produces a different string on every run, so the server and client never agree.

App.jsxApp.jsx
export default function Header() {
  return <h1>Last updated: {new Date().toISOString()}</h1>;
}

The server renders one timestamp into the HTML, then the client renders a new one, and hydration fails. Render a stable value first and update it only after the page is interactive.

App.jsxApp.jsx
import { useEffect, useState } from "react";
 
export default function Header() {
  const [now, setNow] = useState("");
 
  useEffect(() => {
    setNow(new Date().toISOString());
  }, []);
 
  return <h1>Last updated: {now || "just now"}</h1>;
}

Both server and client render "just now" on the first pass, so hydration succeeds. The Effect fills in the real time after mount.

Two-pass rendering for browser-only UI

When part of the UI only exists in the browser, render a placeholder first and switch after hydration.

App.jsxApp.jsx
import { useEffect, useState } from "react";
 
export default function MediaStatus() {
  const [isClient, setIsClient] = useState(false);
 
  useEffect(() => {
    setIsClient(true);
  }, []);
 
  return <p>{isClient ? "Online" : "Checking..."}</p>;
}

The first render matches the server output, so hydration passes. After hydration the component renders again with the browser-only branch. This renders twice, which costs a little time on slow connections.

Suppress only what you cannot fix

For a value that is legitimately different, such as a timestamp, add suppressHydrationWarning to that element. It silences the warning one level deep and is an escape hatch, not a fix.

Do not sprinkle it across the page to make warnings go away. The mismatch still exists underneath, and React may attach handlers to the wrong elements.

Verify the fix

Reload the page with the console open. The warning should be gone, and the first visible frame should match what the server sent. For a systematic walk through the failure, see how to debug React applications systematically.

Hydration is the bridge between server rendering and an interactive app. See how streaming SSR works with React Suspense for the server side of that bridge, where boundaries stream content in after the shell.

Rune AI

Rune AI

Key Insights

  • Hydration fails when server and client markup diverge.
  • Keep browser-only code out of the render path.
  • Render the same data on the server and client.
  • Use two-pass rendering for intentional client-only UI.
  • suppressHydrationWarning is a one-level escape hatch.
RunePowered by Rune AI

Frequently Asked Questions

What does the hydration failed error mean?

The HTML the server sent does not match what the client rendered on its first pass, so React cannot safely attach to the existing DOM.

Is suppressHydrationWarning a real fix?

It silences the warning one level deep but does not fix the underlying mismatch. Use it only for values that legitimately differ, like a timestamp.

Conclusion

Hydration fails when server and client render different markup. Match the two renders, move browser-only work out of render, and use two-pass rendering or suppression only for genuine differences.