Hydration Proof

Search documentation

Find a page or section

Fix a browser extension hydration error

Not your code. Confirm it, then move on.

A browser extension hydration error is not a bug in your code. An extension such as Grammarly, ColorZilla or a password manager adds attributes or elements to the page after the server HTML arrives and before React hydrates, so React finds markup it did not render. Confirm it in a clean browser profile, then ignore it.

Symptoms

The warning names an attribute you never wrote, usually on <body>, <html> or a form field:

Warning: Extra attributes from the server: cz-shortcut-listen
Warning: Extra attributes from the server: data-new-gr-c-s-check-loaded,data-gr-ext-installed
A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up.

A Grammarly hydration error is the most common one, and React's own message now says "It can also happen if the client has a browser extension installed which messes with the HTML before React loaded." Seeing cz-shortcut-listen="true"? Next.js is not the cause: ColorZilla adds it to <body>.

Attribute or elementAdded by
data-new-gr-c-s-check-loaded, data-gr-ext-installed, <grammarly-desktop-integration>Grammarly
cz-shortcut-listen="true"ColorZilla
data-lt-installedLanguageTool
bis_skin_checkedCommonly traced to Bitdefender's browser extension
Their own data-* attributes on forms and inputsPassword managers such as LastPass, Dashlane and 1Password
data-darkreader-* and inline stylesDark Reader
__gchrome_uniqueid on inputsChrome and Edge on iPad, not an extension

The __gchrome_uniqueid hydration warning comes from the browser itself: Chrome and Edge on iPadOS add it to form fields (vercel/next.js#77710). It behaves like an extension and has the same answer.

hydration-proof reports recognized extension changes as HP4002, an info finding that does not fail the run with the default ci.failOn: 'error', with the cause Browser extension. Other changes made before hydration are HP4001.

Why a browser extension hydration error happens

Extensions run their content scripts as soon as the page starts loading, often before your JavaScript. They mark <body> to say they are active, add attributes to inputs they manage, or insert their own elements. React then compares its first render with a DOM that someone else already changed.

React 19 handles part of this: unexpected tags in <head> and <body> are skipped instead of failing hydration. Attributes on <html>, <body> and your elements, and text an extension rewrites (a translator, a spell checker), still differ. An extension that inserts a script before <head> can still break hydration entirely (react/react#24430).

How to fix it

There is nothing to fix in your code. Make sure it really is an extension, and keep it out of your reports.

Confirm it in a clean profile

  1. Open the page in a private window with extensions disabled, or in a new browser profile.
  2. Reload with the console open. If the warning is gone, an extension caused it.
  3. Turn extensions back on one at a time to find which one, if you need to know.

If the warning stays in a clean profile, the attribute comes from your own code or a script on the page. See third-party scripts.

Silence the development warning on html and body only

If the warning gets in your way during development, suppressHydrationWarning on <body> (or <html>) tells React to ignore differences in that element's own attributes:

app/layout.tsx
export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body suppressHydrationWarning>{children}</body>
    </html>
  );
}

It works one level deep. It does not cover inputs, text or anything inside <body>, so do not spread it through your components to chase extension attributes: it would hide your own bugs there. See suppressHydrationWarning.

Catch it with ESLint

No lint rule can see an extension. The one related rule, audit-suppress-hydration-warning, accepts suppressHydrationWarning on <html> and <body> by default for this reason, and reports it on other elements where it would hide more.

Catch it in CI

hydration-proof test drives a clean browser, so extensions installed on your machine never reach its results. When you simulate one on purpose (a scenario's initScripts can change the page before hydration), the changes it recognizes show as HP4002 info findings, and real bugs around them still fail the run. If a known extension attribute keeps appearing in a report, ignore it by name:

hydration-proof.config.ts
import { defineConfig } from "hydration-proof";
 
export default defineConfig({
  ignore: {
    attributes: ["cz-shortcut-listen", /^data-gr-/],
  },
});

Ignored findings stay in the report, marked as ignored, and do not fail the run. See ignoring findings.