Short answer
Find what renders differently on the server and in the browser. Most often it’s a browser-only value read during render (window, localStorage, the current time, a random number): move it into useEffect, so the first browser render matches the server. If that isn’t it, check for invalid HTML nesting, such as a <div> inside a <p> or a link inside a link, then test in a private window without extensions.
What makes the server and browser disagree, most common first
01
Browser-only values read during render
“During React hydration, useEffect is called. This means browser APIs like window are available to use without hydration mismatches.” Next.js, Text content does not match server-rendered HTML
The server has no window, localStorage or user time zone, so code that checks whether window exists, reads localStorage.getItem() or calls new Date() in a component’s render gives the server one answer and the browser another. Render the same thing on both, then switch to the browser value after mount.
const [isClient, setIsClient] = useState(false); useEffect(() => setIsClient(true), []); return <span>{isClient ? new Date().toLocaleTimeString() : ''}</span>;02
HTML nested in a way browsers don’t allow
A <div> or a list inside a <p>, a <p> inside another <p>, a link inside a link or a button inside a button. Browsers can auto-correct invalid nesting while parsing, so the HTML no longer matches what React renders. The error often points at the element.
03
A browser extension or iOS changed the page
“iOS attempts to detect phone numbers, email addresses, and other data in text content and convert them into links, leading to hydration mismatches.” Next.js, Text content does not match server-rendered HTML
Extensions that modify the page edit the HTML before React hydrates. If the error disappears in a private window with extensions off, it isn’t your code. On iPhone, Safari turns phone numbers, emails and addresses into links unless you turn that off with a format-detection meta tag.
<meta name="format-detection" content="telephone=no, date=no, email=no, address=no" />04
A CDN or proxy rewrote the HTML
If the error only appears on the live site, check whether your CDN or proxy minifies or injects anything into the HTML. The Next.js docs name a misconfigured Edge or CDN layer as a cause, with Cloudflare Auto Minify as the example. An incorrectly configured CSS-in-JS library is on the same list.
05
Content that really is different, like a timestamp
“This only works one level deep, and is intended to be an escape hatch. Don’t overuse it.” Next.js, Text content does not match server-rendered HTML
When a value has to differ, such as a time rendered on the server, add suppressHydrationWarning to that one element. It covers that element only, one level deep, so it isn’t a fix for a whole component. For a component that can only work in the browser, load it with next/dynamic and ssr: false from a Client Component.
Checked against the vendor’s docs on : Next.js docs: Text content does not match server-rendered HTML; Next.js docs: Lazy loading (ssr: false); React reference: hydrateRoot.
Rather hand it over?
Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.
- from $450App Fix TicketOne reproducible bug in an existing mobile or web app, diagnosed and fixed.See scope and start
- from $450AI-Built App RescueA Lovable, Bolt or Cursor app checked end to end in 3 days, then its worst problems fixed at a fixed price.No issues worth fixing, no charge. If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way.See scope and start
Related: Lovable app blank after publishing · Build fails on Netlify or Vercel · Vercel environment variables undefined