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
| Text | Fix guide |
|---|---|
| "Updated 3 minutes ago", a clock, a countdown | Time-dependent values |
| A date or time of day | Timezone differences |
| A price or number in the user's format | Locale formatting |
| A random id, greeting or order | Random values |
A name or count from localStorage | Storage |
How to fix it
-
Find the text. Run the page in development and read the warning, or run hydration-proof against the production build (below).
-
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>; } -
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.