Minified React error #425: Text Mismatch After Deploy
Decode #425 and its companion #423, reproduce both triggers from the Stack Overflow thread on Next.js 12.2.5, and see what React 19 prints instead.
TL;DR#
Minified React error #425 is React 18's production code for "Text content does not match server-rendered HTML." Some text node in the HTML that Next.js sent differs from what the browser rendered during hydration. The two causes in the Stack Overflow thread both reproduce on Next.js 12.2.5 with React 18.2.0: a date or time computed during render, and a plain <style> tag whose CSS contains a > selector. Compute the value in useEffect, and write inline CSS with <style jsx> or dangerouslySetInnerHTML.
This is what a production build prints in the browser for the first cause (Next.js 12.2.5, React 18.2.0, Chromium, reproduced on 10 October 2026):
[pageerror] 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.
[pageerror] Minified React error #423; visit https://reactjs.org/docs/error-decoder.html?invariant=423 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.The question itself only carries the #425 code in its title; the third answer in the thread quotes the pair as Uncaught Error: Minified React error #425; and Uncaught Error: Minified React error #423;. The next section explains why React reports a recovered mismatch as uncaught. The [pageerror] label comes from the test harness: Playwright reports errors the page raised to the browser under that name.
Decoding #425, and why #423 arrives with it#
The numbers map to messages in React's error code table. At the v18.2.0 tag, the hydration codes you meet in a Next.js 12 app on React 18 read:
418: Hydration failed because the initial UI does not match what was rendered on the server.
422: There was an error while hydrating this Suspense boundary. Switched to client rendering.
423: There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.
425: Text content does not match server-rendered HTML.So #425 is the specific finding (a text node differs) and #423 is the consequence when the mismatch sits outside any Suspense boundary, as on the reproduction pages: React throws the server HTML away and renders the whole tree in the browser. Inside a Suspense boundary the consequence is #422 instead, and only that boundary is rendered again. #418 is the sibling for structure: in the react-dom@18.2.0 development source it is thrown from tryToClaimNextHydratableInstance, when React finds no server node, or the wrong kind of node, where the client tree expects one.
The page usually looks fine afterwards, which is why the error is easy to ignore. In the reproduction below the visible text was the browser's value, because the client render replaced the server markup. On these pages the cost is a full client render on every load, and with it the loss of the benefit of server rendering for that root.
Why "Uncaught" when nothing crashed? These are recoverable errors. In the react-dom@18.2.0 production bundle, the default handler is reportError when the browser has it, falling back to console.error; the comment beside the same line in the development source says that in modern browsers it "will dispatch an error event, emulating an uncaught JavaScript error". Next.js 12.2.5 calls ReactDOM.hydrateRoot(domEl, reactEl) without passing its own handler, so the default applies and the console labels the report as uncaught.
Get the full message before you change anything#
The number tells you the class of bug, not the element. React's error decoder page only repeats the message. The useful output is in development mode, where the same mismatch names both strings. Run next dev, open the page, and read the console. For the <style> case from the thread, Next.js 12.2.5 printed:
[console.error] Warning: Text content did not match. Server: "%s" Client: "%s"%s .a > .b { color: red; } .a > .b { color: red; }
at style
at div
at StyleThe format string comes first, then its arguments: the server sent .a > .b { color: red; }, the client rendered .a > .b { color: red; }, and the component stack points at the <style> element inside Style. For the date case, the same warning showed two different times, 21:57:05 on the server and 21:57:07 in the browser.
If dev mode does not reproduce it, compare the HTML directly: on a Pages Router build like this one, next build wrote the static pages to .next/server/pages/*.html, and searching that file for the text in question shows what the server committed to.
Cause 1: a date or time computed during render#
The question has no accepted answer; the top-voted one points at date-fns in the dependency list, and the Next.js hydration error page lists "time-dependent APIs such as the Date() constructor in your rendering logic" among its common causes. The minimal page:
import { format } from "date-fns";
export default function Home() {
const time = format(new Date(), "HH:mm:ss");
return <div>Rendered at {time}</div>;
}This page has no getServerSideProps, so next build prerenders it once (○ / in the build output) and the HTML carries the build time: Rendered at 22:11:38. The browser computed 22:11:59 when it loaded the page, so hydration hit #425 then #423.
That detail matters for the question's own example. A format such as MM.dd.yyyy looks harmless because the server and the browser agree within the same day, but on a statically prerendered page the server value is frozen at build time. Every visit on a later day than the build differs. A server rendering in UTC and a reader in another timezone can also disagree near midnight.
Fix: render the value after hydration#
- Render a stable placeholder on the server and in the first client render:
import { useEffect, useState } from "react";
import { format } from "date-fns";
export default function Fixed() {
const [time, setTime] = useState(null);
useEffect(() => {
setTime(format(new Date(), "HH:mm:ss"));
}, []);
return <div>Rendered at {time}</div>;
}- Rebuild and serve the production build, because the minified error only exists there:
npx next build && npx next startThe effect runs only in the browser after hydration, so both renders agree on the empty value and the time appears one render later. This is the pattern React documents as two-pass rendering, and the hydration mismatch guide covers the other date options (UTC everywhere, suppressHydrationWarning) with their trade-offs.
suppressHydrationWarning is the documented answer for a timestamp: both the hydrateRoot reference and the Next.js error page give a timestamp as the example of content that will inevitably differ. Know what it does before you reach for it on this page: React "will not attempt to patch mismatched text content". The warning goes away, but the text from the server HTML is what stays on screen, and on a statically prerendered page that is the time of the build. Use it only for a value you accept being stale until the next render; use the effect above when the reader must see the current value.
Cause 2: a plain <style> tag with a > selector#
The third answer in the thread reports both errors and a one-word fix: replace <style> with <style jsx>. It reproduces, and the prerendered HTML shows why. This page:
export default function Style() {
return (
<div className="a">
<style>{".a > .b { color: red; }"}</style>
<p className="b">Plain style tag</p>
</div>
);
}was written to the static HTML as:
<div id="__next"><div class="a"><style>.a > .b { color: red; }</style><p class="b">Plain style tag</p>The server escaped > as > like any other text. The browser does not decode entities inside <style>, so the text node it holds is literally >, while the client render produces >. Two strings, one text node: #425 and #423 on load.
The failing page and three controls on the same build, each one change away from it:
| Variant | Server HTML | Result |
|---|---|---|
<style> with .a > .b | .a > .b | #425, #423 |
<style> with .a .b (no >) | unchanged | no error |
<style jsx> with .a > .b | no <style> in the markup, scoped jsx-… class names | no error |
dangerouslySetInnerHTML with .a > .b | .a > .b | no error |
So the trigger is the escaped character, not the <style> tag alone. Fix it by writing the CSS with styled-jsx, which Next.js 12.2.5 depends on and needs no install:
export default function StyleJsx() {
return (
<div className="a">
<style jsx>{`
.a > .b {
color: red;
}
`}</style>
<p className="b">styled-jsx style tag</p>
</div>
);
}Or keep a raw tag and pass the CSS as HTML, which is only safe for a constant string you wrote yourself:
export default function StyleInner() {
return (
<div className="a">
<style dangerouslySetInnerHTML={{ __html: ".a > .b { color: red; }" }} />
<p className="b">Style tag via dangerouslySetInnerHTML</p>
</div>
);
}Verify the fix in a production build#
Dev mode is not enough: confirm the minified error is gone where it appeared. After next build && next start, load each page with the console open. In the reproduction, the failing pages and their fixed variants returned:
--- GET / -> body: "Rendered at 22:11:59"
[pageerror] 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.
[pageerror] Minified React error #423; visit https://reactjs.org/docs/error-decoder.html?invariant=423 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
--- GET /fixed -> body: "Rendered at 22:12:01"
(no console errors)
--- GET /style -> body: "Plain style tag"
[pageerror] 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.
[pageerror] Minified React error #423; visit https://reactjs.org/docs/error-decoder.html?invariant=423 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
--- GET /style-noangle -> body: "Plain style tag, no angle bracket"
(no console errors)
--- GET /style-jsx -> body: "styled-jsx style tag"
(no console errors)
--- GET /style-inner -> body: "Style tag via dangerouslySetInnerHTML"
(no console errors)If nothing in your own code computes a value during render and you still see #425 only on the deployed site, compare the HTML your host serves with the file in .next. The Next.js error page also names an edge or CDN that modifies the HTML response, citing Cloudflare Auto Minify, among the causes. A different post-deploy console error, ChunkLoadError, is not a hydration problem at all: it is stale HTML requesting renamed chunks, covered in the ChunkLoadError fix.
One email a month — the latest explained errors
What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.
On React 19 the same bug is #418#
The number depends on the React major. The production bundle of react-dom@18.3.1, the last 18.x release on npm, still has a call site for code 425; the production client bundle of react-dom@19.3.0 has none. The same date page as a Client Component on Next.js 16.4.0 with React 19.3.0 printed:
[pageerror] Minified React error #418; visit https://react.dev/errors/418?args[]=text&args[]= for the full message or use the non-minified dev environment for full errors and additional helpful warnings.The args[]=text argument is what #425 used to say: the mismatch is in text. The useEffect fix above removed it on React 19.3.0 as well. The plain <style> page did not reproduce the error on Next.js 16.4.0 with React 19.3.0; there it was a Server Component in the App Router, so treat that as one negative result, not as proof that the cause is gone. A component marked 'use client' is still rendered on the server first, which is why the date page fails there too. The use client vs use server article explains where that boundary sits, and the localStorage article shows how a typeof window guard swaps a crash for this same class of mismatch.
Hydration errors are one of the ways a green build still ships a broken page, alongside the missing environment variables and Edge imports in the build-passed-but-production-broke postmortem. On React 18, if the error changes from #425 to Minified React error #418 (in dev mode, "Hydration failed because the initial UI does not match"), the mismatch is in the structure rather than in text. On React 19, #418 covers both cases: its message is "Hydration failed because the server rendered %s didn't match the client", and the production bundle fills that placeholder with text or HTML, so read the first args[] value in the URL to tell them apart. For either, continue with the eight causes of hydration mismatch, starting with invalid HTML nesting and browser extensions.
Further reading: Fix: error while hydrating, root switched to client render.
Frequently Asked Questions
Still stuck? Paste your error.
Paste the error message or stack trace — we check it against every fix and tool on the site.
Continue Reading
Fix: error while hydrating, root switched to client render
Why this error takes down the whole page instead of one component, and the two separate causes behind it: broken DOM nesting and no Suspense boundary above the failure.
Fix localStorage is not defined after 'use client' (Next.js)
The cookie banner from the Stack Overflow question, rebuilt on Next.js 16.3.8: why 'use client' does not stop the crash, the useEffect fix, and the other errors Node 25 and 26 print.
Fix: react-dom/server Import Error in Next.js App Router
"You're importing a component that imports react-dom/server" is a compile-time rule of the App Router, not a bug in your code. Here is what the rule protects, which use cases are legitimate, and the fixes verified against Next.js 15.5 and 16.3 with both webpack and Turbopack.


