Hydration Proof

Search documentation

Find a page or section

Minified React error #425

React 18's production code for text that differs between the two renders.

Minified React error #425 is React 18's production code for "Text content does not match server-rendered HTML": a text node in the first browser render differs from the server's HTML. It is almost always a date, a time, a formatted number or a random value. Find the text, then make both renders produce it.

The error

Minified React error #425; visit https://reactjs.org/docs/error-decoder.html?invariant=425 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.

It is usually followed by #423 (React rendered the whole page again) or #422 (one Suspense boundary).

The full message is:

Text content does not match server-rendered HTML.

React 19 no longer uses this code. A text mismatch there is #418, with text as its argument. If you see #425, the page runs React 18.

What minified React error #425 means

React error 425 carries no values, and in production React 18 does not print the warning that names them. In development, the same mismatch logs:

Warning: Text content did not match. Server: "5:00 AM" Client: "10:00 AM"

To find the text, reproduce it in development or compare the page yourself. A difference that appears only in production usually comes from the server's environment: servers usually run in UTC and with a default locale, while your users' browsers do not. A statically built page also keeps the text from the moment it was built.

Common causes

TextFix guide
"Updated 3 minutes ago", a clock, a countdownTime-dependent values
A date or time of dayTimezone differences
A price or number in the user's formatLocale formatting
A random id, greeting or orderRandom values
A name or count from localStorageStorage

How to fix it

  1. Find the text. Run the page in development and read the warning, or run hydration-proof against the production build (below).

  2. Format it with fixed options. Before, the time uses each side's timezone:

    components/signed-in-at.tsx
    "use client";
     
    export function SignedInAt({ at }: { at: string }) {
      return <p>Signed in at {new Date(at).toLocaleTimeString()}</p>;
    }

    After, the locale and timezone are explicit, so the server and the browser produce the same text. Pass the user's timezone from the server (a cookie or a profile setting) to show local time:

    components/signed-in-at.tsx
    "use client";
     
    export function SignedInAt({
      at,
      timeZone,
    }: {
      at: string;
      timeZone: string;
    }) {
      const time = new Date(at).toLocaleTimeString("en-US", { timeZone });
      return <p>Signed in at {time}</p>;
    }
  3. Render live values after hydration, for clocks and "time ago" labels. See useEffect and two-pass rendering.

Find every instance

hydration-proof reports each text difference as HP1001, with both values and the likely cause, in the production build:

npx hydration-proof test --probe

--probe proves the cause by changing one thing at a time: the clock, the locale or the timezone. The ESLint rules no-date-in-render and no-timezone-without-explicit-timezone catch the code before it ships.