Skip to content

What is a hydration error?

A hydration error happens when the HTML a server rendered does not match what the browser's JavaScript renders, so the framework warns or re-renders.

Last updated , 9 min read

What is a hydration error?

A hydration error is a web app failure in which the server's HTML differs from what the browser's JavaScript renders on page load. Hydration is the step in which a framework, e.g. React, attaches event handlers to HTML that the server already sent. The difference itself is called a hydration mismatch.

React's documentation says hydration "turns the initial HTML snapshot from the server into a fully interactive app." The browser's render must match the server's HTML. When it does not, React reports an error and renders the page, or part of it, again in the browser. Next.js, a framework built on React, reports the same errors.

The page often looks right afterward, because the browser's render replaces the server's HTML. The React documentation warns that a mismatch can slow the page, and in the worst case attach event handlers to the wrong elements. A page that renders only in the browser has nothing to hydrate, and a command-line application has no page.

What causes a hydration error?

A page that the server renders goes through hydration in five steps:

  1. The server runs the page's components and sends the HTML to the browser.
  2. The browser shows the HTML, which does not respond to clicks yet.
  3. The browser loads the page's JavaScript, and React runs the same components again.
  4. React matches its result to the HTML in the page and attaches event handlers.
  5. If an element or its text differs, React reports a hydration error and renders the page again, or only the <Suspense> section around the difference.

React does not correct a differing attribute, and warns about it only in development.

Where a hydration error happens Server renders HTML Browser shows the HTML React renders again Compare with the HTML Match handlers attached Mismatch reports an error renders in browser
Both renders must give the same result. The error happens at the comparison, after the browser has already shown the server's HTML.

React's error message and the Next.js hydration error page list these common causes:

  • Browser-only values. A branch on typeof window !== 'undefined' or a value read from localStorage renders one thing on the server, where neither exists, and another in the browser.
  • Changing values. A call that returns a different value each time, e.g. Date.now(), gives each render a different result.
  • Locale and time zone. Formatting a date or a price with default settings gives the server's format on the server and the customer's in the browser.
  • Invalid HTML nesting. The browser's parser repairs invalid markup, e.g. a <div> inside a <p>, so the page loses the structure React rendered.
  • Changes from outside the app. A browser extension or a proxy can change the HTML before React loads. On iOS, the browser can turn phone numbers into links unless a format-detection meta tag turns that off.

What does a hydration error look like?

Here is an illustrative example. Acme Co. sells furniture online, and its web store renders pages on the server with Next.js. A developer at Acme asks a coding agent to "Show the order total in the customer's own number format." The change fails in six steps:

  1. The agent formats the total in US dollars with toLocaleString and the runtime's default locale.
  2. The build and unit tests pass, and the agent's output says the task is "done."
  3. The server uses the en-US locale and sends $240.00 in the HTML.
  4. A customer with a German browser opens the checkout page.
  5. React renders 240,00 $, reports the error below, and renders the checkout page again.
  6. The customer sees the total change format after the page appears.

React 19.2 and later report this message in development, shortened here:

Hydration failed because the server rendered text didn't match the client.
As a result this tree will be regenerated on the client. ...

https://react.dev/link/hydration-mismatch

  <Checkout>
    <section>
      <h2>
      <OrderTotal total={240}>
        <p>
+         240,00 $
-         $240.00

The + line is the browser's render, and - is the server's HTML. For an element, or in React 19.0 and 19.1, the message says "HTML" instead of "text." A production build shows only Minified React error #418 and a link. In React 18, this text difference gave Text content does not match server-rendered HTML. An element difference gave "Hydration failed because the initial UI does not match what was rendered on the server."

This example is simplified. A real store would check more values, e.g. delivery dates.

What changes when a coding agent writes the code?

A coding agent usually checks a web change from the terminal, e.g. with the build and the unit tests. None of those steps hydrates a page. Component tests in a simulated browser usually render with createRoot, not hydrateRoot, so they skip hydration too.

Since version 16.2, next dev forwards browser errors to the terminal by default, once a browser loads the page. The Next.js 16.2 release notes say this helps agents that "can't access a browser console."

A quick fix can also cause the error. When server rendering fails with window is not defined, a typeof window check stops the crash and creates a mismatch. An agent asked to clear the warning can add suppressHydrationWarning, which hides the message and keeps the mismatch. Acme's German customer would then see $240.00 and no error.

Both edits look harmless in a diff, which is common for bugs in AI-generated code. When a diff adds either one, a practical adjustment is running code in a second locale and checking the rendered value.

How do you fix or prevent a hydration error?

The fix makes the browser's first render match the server's HTML, in one of these ways:

  • Pass the value from the server. Decide the value on the server, e.g. the locale from the request, and pass it down as a prop.
  • Render browser values after hydration. Set the value in a useEffect hook, which runs only in the browser. The component renders twice, so hydration takes longer.
  • Skip the server render. In a Next.js Client Component, dynamic() with ssr: false renders one component in the browser only.
  • Silence one unavoidable difference. suppressHydrationWarning hides the warning for a value that must differ, e.g. a timestamp. It works one level deep, and React leaves the server's text in place.

The Acme fix passes the locale down:

function OrderTotal({ total, locale }) {
  // locale comes from the server, e.g. "de-DE" from Accept-Language
  const text = total.toLocaleString(locale, {
    style: "currency",
    currency: "USD",
  });
  return <p>Total: {text}</p>;
}

A smoke test can open each page the server renders and fail on a hydration message, or on error #418 in production. A browser test framework, e.g. Playwright, can run it in a headless browser with a locale and timezoneId that differ from the server's. React 19 reports a text or element mismatch as an uncaught page error, not a console message, so the test should listen for both. In Chromium, Playwright receives both over the Chrome DevTools Protocol.

How is a hydration error different from a runtime error?

A runtime error is an exception that stops code while it runs, e.g. a TypeError from reading a property of undefined. A hydration error also happens at runtime, but each render succeeds, and the fault is the difference between them.

React recovers from a text or element mismatch by rendering again, so the page keeps working. An error thrown while rendering removes the whole React tree, unless an error boundary catches it. A stack trace points at a line of code, and a hydration error points at a component and a diff. Either message works as an implicit test oracle, a failure signal that needs no expected value.

How do you find hydration errors before users do?

An end-to-end test that clicks through the app can miss the error. In Next.js, a page reached through a link inside the app renders without server HTML, so only the first page hydrates. Open each page that a change touched with a direct visit instead, and treat a hydration message as a failure.

A passing build and passing unit tests do not show how a changed page behaves when it runs. RunStory runs your software in a separate environment, tries relevant workflows, and checks the results. It is in private alpha for CLIs and web apps.

Join the RunStory alpha →

FAQs

What does the React hydration mismatch warning mean?

The React hydration mismatch warning means that React's first render in the browser differs from the server's HTML. In React 19 the message starts "Hydration failed because the server rendered HTML didn't match the client," and React 19.2 and later say "text" for a text difference. React then renders the page, or part of it, again.

Can suppressHydrationWarning fix a hydration error?

The suppressHydrationWarning prop hides the warning on one element, but it does not fix the mismatch. React leaves the server's text in place, so a customer can see the wrong format. Use it only for a value that must differ, e.g. a timestamp.

Do hydration errors break the page for users?

Hydration errors often leave the page working, because React renders the page, or part of it, again. Users can still see content change after it appears, and the page can load more slowly. In the worst case, event handlers can get attached to the wrong elements.

Why does a hydration error show up in only some browsers?

A hydration error shows up in only some browsers when the difference comes from the browser itself. The locale and time zone change how prices and dates are formatted. Browser extensions and iOS format detection can also change the HTML.