Hydration Proof

Search documentation

Find a page or section

HP1001: Text differs between server and client

HP1001 (text mismatch) means the server HTML has different text than React rendered in the browser. The likely causes, the fix for each, and an example.

HP1001 (text-mismatch) means the server and browser rendered different text. The finding shows both values, the element and, when available, its source line. Check whether the component reads the clock, locale or browser storage during render. Give both sides the same initial value.

CodeHP1001
Nametext-mismatch
Default severityError
GroupDOM mismatches
What it meansThe server HTML contains different text than the browser rendered during hydration.

What HP1001 (text-mismatch) means

React expects the first browser render to match the server's text. If it does not, React discards that part of the server HTML and renders it again. The user may see a flash or lose input entered before hydration finished.

hydration-proof checks both sides of the difference:

  • It sees text React replaced during hydration.
  • It also compares text in elements React kept, so a difference is not missed just because React left the server node in place.

If React re-rendered the surrounding branch, the evidence says which branch it discarded. You do not get a second HP1010 finding for the same problem. A whitespace-only difference is HP1015 instead.

The React error it matches

In development, React usually reports a text mismatch too. hydration-proof keeps React's message with the finding. The wording depends on the React version and build:

Hydration failed because the server rendered text didn't match the client.
Text content does not match server-rendered HTML.
Warning: Text content did not match. Server: "5:00 AM" Client: "10:00 AM"
Minified React error #418; visit https://react.dev/errors/418

Text content does not match server-rendered HTML covers the React 18 wording, and the server rendered HTML didn't match the client the React 19 one.

Likely causes

Start with the two values in your report. They often point to one of these causes:

CauseWhat differs
Time-dependent valueDate.now() or new Date() read during render
Timezone differenceA date formatted in the server's and the browser's timezone
Locale-dependent formattingtoLocaleString() with the runtime's default locale
Random valueMath.random() or crypto.randomUUID() in render
Browser storagelocalStorage read behind a typeof window check
Media querymatchMedia or the window size read during render
Browser-only APInavigator.userAgent and other browser globals
Different dataThe client fetched the data again and got a new result

How to fix it

The first browser render must use the same text as the server. Pass a value from the server as a prop, or show a stable placeholder and update it in useEffect after hydration.

For example, a greeting that reads localStorage renders "guest" on the server and the stored name in the browser:

welcome.tsx
"use client";
 
export function Welcome() {
  const name =
    typeof window === "undefined"
      ? "guest"
      : (localStorage.getItem("name") ?? "guest");
 
  return <p>Welcome back, {name}</p>;
}

Render the value both sides agree on first, and read storage after hydration:

welcome.tsx
"use client";
 
import { useEffect, useState } from "react";
 
export function Welcome() {
  const [name, setName] = useState("guest");
 
  useEffect(() => {
    setName(localStorage.getItem("name") ?? "guest");
  }, []);
 
  return <p>Welcome back, {name}</p>;
}

If the value comes from a date, pass the server's time and an explicit locale and timeZone. If it comes from fetched data, send the data the server used to the client. When the cause is unclear, run hydration-proof test --probe: it reloads the page with one factor changed at a time. See probes for the factors it can test.

Prevent it with ESLint

The ESLint plugin reports the usual sources in render code: no-date-in-render, no-random-in-render, no-locale-without-explicit-locale, no-timezone-without-explicit-timezone, no-storage-in-initial-render and no-window-render-branch.

When the difference is intentional

For a value that is meant to differ, such as a live clock, put suppressHydrationWarning on the element that holds it: React keeps the server text, and hydration-proof lists the difference as HP6001 (info) instead. ignore.textPatterns ignores a text difference when both values are equal after removing the patterns, for example /\d{2}:\d{2}/ for times. See ignoring findings.

Example

  ✖ /status 842ms  1 error
    HP1001 Text differs between server and client  ·  app/status/rendered-at.tsx:4
      server "Rendered at 1767225600000"  → client "Rendered at 1767225600412"
      Possible cause: Time-dependent value.  Fix: Pass the server timestamp as a prop or render the time after mount.

The cause is a suggestion unless a probe confirms it. Open the HTML report for the source code, React's message and a screenshot of the element.