Skip to content

PATTERN Cited by 1 source

Suppress hydration warning for unavoidable mismatch

Pattern. For content whose server-rendered value and client-rendered value are semantically guaranteed to differ (timers, time-deltas, "seconds remaining", live-clock displays), wrap the value in an element and set suppressHydrationWarning={true} on that wrapper. React accepts the local mismatch, uses the client-computed value, and doesn't emit an onRecoverableError.

From React's docs: suppressHydrationWarning on hydrateRoot.

Minimum viable form

Instead of (causes a hydration mismatch every render):

//...
const timeDistance = targetDate.getTime() - Date.now();
return <div>{timeDistance}</div>;

Do (mismatch is expected and suppressed):

//...
const timeDistance = targetDate.getTime() - Date.now();
return <div suppressHydrationWarning={true}>{timeDistance}</div>;

(Source: sources/2023-07-10-zalando-rendering-engine-tales-road-to-concurrent-react.)

Hard rule: one level deep

suppressHydrationWarning={true} only applies to the element it's set on — specifically, its direct text content and attributes. It does not cascade to children. If a component structure is:

<div suppressHydrationWarning={true}>
  <span>{timeDistance}</span> {/* mismatch NOT suppressed here */}
</div>

the <span>'s text mismatch is still reported. Wrap as close to the mismatching text as possible:

<div>
  <span suppressHydrationWarning={true}>{timeDistance}</span>
</div>

When this pattern is correct

  • Time deltas (Date.now() - target) where the difference is the only varying piece between SSR and CSR.
  • Timer countdowns that update every second anyway — the first client render replaces the SSR value within 1s.
  • Random-but-semantically-equivalent content where the SSR picked one of many valid renderings and the CSR might pick a different equally-valid one.

When this pattern is WRONG

  • Locale / timezone mismatches — these can be fixed by passing explicit args or by moving localization to the backend. See patterns/backend-localization-for-hydration-stability. Suppressing them hides real bugs.
  • Content that genuinely differs SSR vs CSR by product requirement — use patterns/mount-gated-client-only-rendering instead, which renders a consistent-across-sides fallback until mount.
  • Invalid HTML nesting — suppression doesn't make the browser's parser suddenly agree; fix the nesting.
  • Anything you'd rather fix but don't have time for. Suppression is semantically "this mismatch is correct," not "mute this error." Using it as a generic silencer pollutes the meaning.

Caveats

  • First-render-only effect. suppressHydrationWarning is checked only at hydration; it has no effect on subsequent renders (no mismatch is possible past initial hydration).
  • React will still use the client value at the point of mismatch — the server value is replaced. If that causes a visual jump, use patterns/mount-gated-client-only-rendering + a layout-preserving fallback instead.
  • Doesn't affect developer-console warnings in dev mode — only the onRecoverableError callback's production path. (Verify against React version; the post documents this behaviour as of React 18.)
  • suppressHydrationWarning as a prop of text-only children is how React reads it; on elements with sub-components, place it on the smallest element whose text is the mismatching part.

Observability consequence

Every suppressHydrationWarning you add is a category of error you've agreed to silence. Consider logging these cases to your own observability channel (not Sentry-as-error) with a tag so you know which suppressed mismatches actually occurred in production — otherwise you lose signal about clock-drift, timezone-config regressions, and similar environmental changes that might break the semantically-equivalent assumption.

Seen in

Last updated · 501 distilled / 1,218 read