# Fix a browser extension hydration error

> A browser extension hydration error comes from Grammarly, ColorZilla or a password manager editing the page before React hydrates. Confirm it, then ignore it.

Source: https://hydration.jscrate.dev/docs/causes/extension
Last updated: 2026-09-18

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:

```text
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](https://github.com/vercel/next.js/issues/77710)). It
behaves like an extension and has the same answer.

hydration-proof reports recognized extension changes as
[HP4002](https://hydration.jscrate.dev/docs/issues/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](https://hydration.jscrate.dev/docs/issues/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](https://github.com/facebook/react/issues/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](https://hydration.jscrate.dev/docs/causes/third-party-script).

### 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:

```tsx title="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](https://hydration.jscrate.dev/docs/guides/suppresshydrationwarning).

## Catch it with ESLint

No lint rule can see an extension. The one related rule,
[`audit-suppress-hydration-warning`](https://hydration.jscrate.dev/docs/rules/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:

```ts title="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](https://hydration.jscrate.dev/docs/ignoring).

## Related

- [Extra attributes from the server](https://hydration.jscrate.dev/docs/errors/extra-attributes-from-the-server)
- [Scripts that change the page before hydration](https://hydration.jscrate.dev/docs/causes/third-party-script)
- [HP4002: a browser extension changed the page](https://hydration.jscrate.dev/docs/issues/hp4002)
- [When suppressHydrationWarning is safe](https://hydration.jscrate.dev/docs/guides/suppresshydrationwarning)
- [All causes of hydration errors](https://hydration.jscrate.dev/docs/causes)
