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 element | Added by |
|---|---|
data-new-gr-c-s-check-loaded, data-gr-ext-installed, <grammarly-desktop-integration> | Grammarly |
cz-shortcut-listen="true" | ColorZilla |
data-lt-installed | LanguageTool |
bis_skin_checked | Commonly traced to Bitdefender's browser extension |
Their own data-* attributes on forms and inputs | Password managers such as LastPass, Dashlane and 1Password |
data-darkreader-* and inline styles | Dark Reader |
__gchrome_uniqueid on inputs | Chrome 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
- Open the page in a private window with extensions disabled, or in a new browser profile.
- Reload with the console open. If the warning is gone, an extension caused it.
- 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:
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:
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.