Hydration Proof

Search documentation

Find a page or section

HP4001: The page was modified before React hydrated

HP4001 (pre-hydration-mutation) means a script, extension or third-party tag changed the server HTML before React hydrated it. Find the script and move it.

HP4001 (pre-hydration-mutation) means something other than React changed the server-rendered DOM after the browser parsed it and before React hydrated it: a script, a browser extension or a third-party tag. React then hydrates markup it did not render. Find the script and run it after hydration, or keep it out of React's tree.

CodeHP4001
Namepre-hydration-mutation
Default severityError
GroupChanges made outside React
What it meansA script, browser extension or third-party tag changed the server-rendered DOM before hydration.

What the HP4001 pre-hydration mutation finding means

hydration-proof compares the tree the browser parsed from the server HTML with the DOM right before React's first hydration commit. React's own streaming moves (Suspense content revealed from the stream) are undone first, so what is left was done by other code. The finding shows the value on each side:

Text changed before hydration: "Pro" became "Pro (recommended)".

The severity depends on where the change happened:

WhereSeverity
On an element React renders, inside a React rooterror
On <html>, <body> or <head>warning
Outside every React rootinfo (it cannot break hydration)

Some changes are not reported: <link>, <script>, <style>, <meta> and similar tags added to or removed from <head> (loaders, preloads and style injection), changes that look like a browser extension (reported as HP4002), and text or attribute changes on an element marked with suppressHydrationWarning (reported as HP6001).

Likely causes

  • A script changed the page before hydration: A/B testing snippets, personalization or consent tools, translation widgets or an inline script of your own that sets text or classes.
  • A browser extension that rewrites text, such as a translator.
  • A theme script that sets a class on <html> before hydration, without suppressHydrationWarning on that element.

How to fix it

  1. Find the script. The finding names the element and both values, and the HTML report's timeline shows which scripts loaded before hydration. Loading the page with the script blocked in your browser's developer tools confirms it.
  2. Run it after hydration. Load third-party tags after the page is interactive, for example with next/script and strategy="afterInteractive" or "lazyOnload".
  3. Or keep it out of React's tree. A widget that renders into its own container appended to <body> does not touch elements React hydrates.
  4. For theme scripts, mark the element the script changes (usually <html>) with suppressHydrationWarning. The change is then listed as an intentional difference instead.
  5. If a browser extension causes it, it cannot be fixed in code; add an ignore rule for the extension's attributes.

Loading a tag after hydration in Next.js:

app/layout.tsx
import Script from "next/script";
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        {children}
        <Script src="https://cdn.example.com/widget.js" strategy="lazyOnload" />
      </body>
    </html>
  );
}

Example

  ✖ /pricing 1.3s  1 error
    HP4001 The page was modified before React hydrated  (a script changed the page before hydration, 80%)
      #plan-name
      server: "Pro"
      client: "Pro (recommended)"
      → Load the script after hydration (for example next/script with strategy="afterInteractive" or "lazyOnload"), or make it change only elements outside React's tree.

When the change is expected

If a script you cannot move changes an element on purpose, ignore the element with ignore.selectors, or the attribute with ignore.attributes, or add an ignore.issues rule with code: "HP4001" and a reason. See ignoring findings. checks.externalChanges: false turns off every HP4xxx check.