Hydration Proof

Search documentation

Find a page or section

Handle an intentional hydration mismatch

Some differences are fine. Make sure you know which ones you hid.

An intentional hydration mismatch is a difference you accept: a timestamp the server renders and the browser replaces, or a theme class a script sets before hydration. Mark the one element that differs with suppressHydrationWarning, keep it on the smallest element possible, and let hydration-proof list what each suppression hides.

Symptoms

Nothing in the console: that is what the attribute is for. hydration-proof still sees the difference and reports it, so a suppression cannot quietly cover a real bug:

CodeSeverityMeaning
HP6001infoA difference hidden by suppressHydrationWarning, with both values
HP6002errorThe attribute cannot hide this: the elements inside differ
HP6003infoNothing on the element differs, so the attribute hides nothing today

These findings get the cause Intentional difference (suppressHydrationWarning). When the hidden difference does not look intentional (not a time, timezone, locale, random, theme or extension difference), HP6001 is raised to a warning with the note "suppressHydrationWarning hides a difference that does not look intentional", and the fix for the real cause is shown first.

What suppressHydrationWarning does

suppressHydrationWarning tells React to keep the server's text and attributes for one element without reporting that they differ. The React documentation is direct about its limits: "This only works one level deep, and is intended to be an escape hatch. Don't overuse it. React will not attempt to patch mismatched text content."

So the element keeps showing the server's value until something re-renders it, and anything inside a child element is not covered at all.

Which intentional hydration mismatch is safe?

DifferenceSuppress it?
A live clock or "last updated" time that updates right after hydrationYes, on the element that shows the time
The theme class a pre-hydration script sets on <html>Yes, on <html> only
Attributes browser extensions add to <body>Only if the development warning gets in your way
Data, ids, CSS-in-JS class names, invalid nestingNo: fix the cause

When the difference should not exist, the cause pages have the fix: start with all causes of hydration errors.

How to fix it

Put it on the smallest element

The attribute covers the element's own text and attributes. On a wrapper, it misses the text inside a child:

clock.tsx
// Before: still a hydration error, the text is in the <span>
export function ClockBefore({ now }: { now: Date }) {
  return (
    <div suppressHydrationWarning>
      <span>{now.toLocaleTimeString("en-US", { timeZone: "UTC" })}</span>
    </div>
  );
}
 
// After: on the element whose text differs
export function Clock({ now }: { now: Date }) {
  return (
    <div>
      <span suppressHydrationWarning>
        {now.toLocaleTimeString("en-US", { timeZone: "UTC" })}
      </span>
    </div>
  );
}

It also does nothing on a component (<Clock suppressHydrationWarning />) unless the component passes the prop to an HTML element.

Fix structural differences instead

suppressHydrationWarning never covers elements that are added, removed or replaced. If the server renders a <p> and the client a <div> inside the marked element, React still fails hydration, and hydration-proof reports HP6002. Render the same elements on both sides and let only text or attributes differ.

Remove it where nothing differs

HP6003 means the element's server and client output are identical. The attribute is doing nothing today and will hide the next real mismatch on that element. Remove it.

Prefer rendering after mount

Even an intended difference can often be avoided: render a placeholder on the server and the real value in an effect. The page then never shows a stale server value. See useEffect and two-pass rendering and when suppressHydrationWarning is safe.

Catch it with ESLint

audit-suppress-hydration-warning reports suppressHydrationWarning on an element with element children (it only covers one level), on an element whose content is static (nothing can differ, with a suggestion to remove it) and on a component. <html> and <body> are accepted by default. The strict preset reports every other use too, so each suppression needs an eslint-disable comment that explains it. The rule also checks Server Components, since root layouts are one.

npm install -D eslint-plugin-hydration-proof

Catch it in CI

hydration-proof test lists every suppressed difference as HP6001 with the server and client values, so a review can check that each one is the intended timestamp or theme class and nothing else. HP6002 fails the run; HP6001 and HP6003 are info unless the hidden difference looks like a bug.

npx hydration-proof test

To keep a difference out of hydration-proof's results without touching React, use the ignore options or a data-hydration-proof-ignore attribute instead: see ignoring findings.