Hydration Proof

Search documentation

Find a page or section

Report suppressHydrationWarning where it has no effect or hides more than intended.

audit-suppress-hydration-warning is the suppressHydrationWarning ESLint check: it reports the attribute where it does nothing or hides more than you meant, on an element with child elements, on static content, or on a component. With the strict preset it reports every use, so each one needs a comment that explains it.

Rulehydration-proof/audit-suppress-hydration-warning
What it reportsReport suppressHydrationWarning where it has no effect or hides more than intended
recommended / nextError
strictError
Server ComponentsChecked (the markup is the same on both sides)
SuggestionsYes
OptionsallowOn, reportAll

What the suppressHydrationWarning ESLint rule reports

suppressHydrationWarning (not ={false}) on:

  • an HTML element with element children (tooDeep). The attribute only covers the element's own attributes and its direct text, one level deep. A mismatch inside a child is still an error.
  • an HTML element whose attributes and text are all static (unused). Nothing can differ, so the attribute only hides future mistakes. key, ref and event handlers are ignored when deciding. A suggestion removes the attribute.
  • a component (onComponent), such as <Clock suppressHydrationWarning />. It has no effect unless the component passes the prop to an HTML element.
  • with reportAll: true (the strict preset), every other use (audit), so each suppression needs an eslint-disable comment that explains it.

<html> and <body> are accepted by default (option allowOn): theme scripts and browser extensions change their attributes before React hydrates, which is the use case the attribute exists for.

Unlike most rules, this rule also checks Server Components: the root layout, where <html suppressHydrationWarning> usually lives, is one.

Why

suppressHydrationWarning tells React to keep the server's text and attributes for one element without reporting a difference. Used in the wrong place it either does nothing (the error still happens) or hides a real bug:

<div suppressHydrationWarning>
  <span>{new Date().toLocaleTimeString()}</span>{" "}
  {/* still a hydration error: the text is in the span */}
</div>

hydration-proof test lists every difference hidden by the attribute as HP6xxx info (HP6001 to HP6003), so you can check what each one suppresses. When suppressHydrationWarning is safe covers the cases where the attribute is the right tool.

Incorrect

function Clock() {
  return (
    <div suppressHydrationWarning>
      <span>{new Date().toLocaleTimeString("en-US", { timeZone: "UTC" })}</span>
    </div>
  );
}
 
function Title() {
  return <h1 suppressHydrationWarning>Dashboard</h1>;
}
 
function Page() {
  return <RelativeTime suppressHydrationWarning />;
}

With reportAll: true, as in the strict preset, a use that is not obviously wrong is reported too, until it has a comment that explains it:

function Clock({ now }) {
  return (
    <span suppressHydrationWarning>
      {now.toLocaleTimeString("en-US", { timeZone: "UTC" })}
    </span>
  );
}

Correct

function Clock({ now }) {
  return (
    <div>
      <span suppressHydrationWarning>
        {now.toLocaleTimeString("en-US", { timeZone: "UTC" })}
      </span>
    </div>
  );
}
 
function Title() {
  return <h1>Dashboard</h1>;
}
 
export function RootLayout({ children }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body>{children}</body>
    </html>
  );
}

With reportAll: true, a use with an eslint-disable comment that gives the reason:

function Clock({ now }) {
  return (
    <div>
      {/* eslint-disable-next-line hydration-proof/audit-suppress-hydration-warning -- a live clock; the server time is replaced after hydration */}
      <span suppressHydrationWarning>
        {now.toLocaleTimeString("en-US", { timeZone: "UTC" })}
      </span>
    </div>
  );
}

Options

OptionTypeDefaultDescription
allowOnstring[]['html', 'body']: theme and extension scripts change their attributes before React hydratesElements where any use is accepted.
reportAllbooleanfalseReport every other use too, so each one must be justified in a disable comment.
eslint.config.mjs
{
  rules: {
    'hydration-proof/audit-suppress-hydration-warning': ['error', { allowOn: ['html', 'body', 'ThemeProvider'], reportAll: false }],
  },
}
  • allowOn (string array, default ['html', 'body']): element or component names where any use is accepted. Set it to [] to check <html> and <body> as well.
  • reportAll (boolean, default false): also report uses that are not obviously wrong. The strict preset turns this on.

Messages

What ESLint prints for this rule, word for word:

  • suppressHydrationWarning on <<tag>> only covers its own attributes and text, not the elements inside it: a mismatch in a child is still an error. Put it on the element whose content differs.
  • suppressHydrationWarning on <<tag>> has nothing to suppress: its attributes and text are static. Remove it so that real mismatches added later are not hidden.
  • suppressHydrationWarning on <<tag>> does nothing unless the component passes it to an HTML element. Put it on the element that renders the differing content, or make sure <tag> forwards it.
  • suppressHydrationWarning hides hydration mismatches on <<tag>>. Render the same value on the server and in the browser instead, or explain why it is needed in an eslint-disable comment.
  • Remove suppressHydrationWarning.

When not to use it

When a design system passes suppressHydrationWarning through many wrapper components on purpose: add those components to allowOn instead of turning the rule off.