# Handle an intentional hydration mismatch

> An intentional hydration mismatch is a difference you accept, like a timestamp. Put suppressHydrationWarning on the smallest element and audit what it hides.

Source: https://hydration.jscrate.dev/docs/causes/suppressed
Last updated: 2026-09-18

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:

| Code                          | Severity | Meaning                                                              |
| ----------------------------- | -------- | -------------------------------------------------------------------- |
| [HP6001](https://hydration.jscrate.dev/docs/issues/hp6001) | info     | A difference hidden by `suppressHydrationWarning`, with both values  |
| [HP6002](https://hydration.jscrate.dev/docs/issues/hp6002) | error    | The attribute cannot hide this: the elements inside differ           |
| [HP6003](https://hydration.jscrate.dev/docs/issues/hp6003) | info     | Nothing 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?

| Difference                                                             | Suppress it?                                     |
| ---------------------------------------------------------------------- | ------------------------------------------------ |
| A live clock or "last updated" time that updates right after hydration | Yes, 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 nesting                      | No: fix the cause                                |

When the difference should not exist, the cause pages have the fix: start with
[all causes of hydration errors](https://hydration.jscrate.dev/docs/causes).

## 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:

```tsx title="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](https://hydration.jscrate.dev/docs/guides/useeffect-two-pass-rendering)
and [when suppressHydrationWarning is safe](https://hydration.jscrate.dev/docs/guides/suppresshydrationwarning).

## Catch it with ESLint

[`audit-suppress-hydration-warning`](https://hydration.jscrate.dev/docs/rules/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.

```bash
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.

```bash
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](https://hydration.jscrate.dev/docs/ignoring).

## Related

- [suppressHydrationWarning: when it is safe](https://hydration.jscrate.dev/docs/guides/suppresshydrationwarning)
- [HP6001: mismatch hidden by suppressHydrationWarning](https://hydration.jscrate.dev/docs/issues/hp6001)
- [The audit-suppress-hydration-warning rule](https://hydration.jscrate.dev/docs/rules/audit-suppress-hydration-warning)
- [Dark mode and the class on html](https://hydration.jscrate.dev/docs/causes/theme)
- [Date and time values](https://hydration.jscrate.dev/docs/causes/time)
