Hydration Proof

Search documentation

Find a page or section

Extra attributes from the server

React 18's warning for attributes that something else added.

"Extra attributes from the server" is a React 18 development warning: an element has attributes that React's first client render does not produce. Usually something changed the HTML before React loaded: a theme script that adds class and style to <html>, or a browser extension that marks <body>. It is a warning, not a failed hydration.

The error

React 18 lists the attribute names, separated by commas:

Warning: Extra attributes from the server: class,style
Warning: Extra attributes from the server: style
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

The component stack under it ends at the element, often at body or at html. Production builds do not check attributes, so there is no minified code.

React 19 dropped the separate warning. The same attributes appear as - lines in the diff of "A tree hydrated but some attributes… didn't match", for example - cz-shortcut-listen="true".

What extra attributes from the server means

React 18 compares the attributes of each hydrated element with the props you rendered. Attributes that exist in the page but not in your props are "extra". React does not remove them and does not render the page again: it logs the warning once and continues.

The name is misleading. "From the server" means "in the DOM React found", which includes anything a script or an extension added after the HTML arrived. Your server often never sent these attributes.

Common causes

AttributeAdded byWhat to do
class, style on <html>next-themes or your own theme script, before hydrationAdd suppressHydrationWarning to <html>. See theme
cz-shortcut-listen on <body>The ColorZilla extensionNothing in your code. See extensions
data-new-gr-c-s-check-loaded, data-gr-ext-installedGrammarlyNothing in your code
Other data-* or styleA/B testing, analytics or chat scriptsLoad them after hydration. See third-party scripts
Attributes on many elementsA CDN or proxy rewriting HTMLTurn the rewriting off. See CDNs

Extensions are a known problem with no fix on React's side yet: React issue #24430 about them is still open.

How to fix it

  1. Read the attribute names and decide who added them, using the table.

  2. Test in a private window or a clean browser profile. If the warning disappears, an extension added the attribute. Your users will see it too, but nothing is broken.

  3. For a theme script, mark <html> as expected to differ. next-themes sets class and style on <html> before React loads, on purpose, to avoid a flash of the wrong theme:

    app/layout.tsx
    import { ThemeProvider } from "next-themes";
    import type { ReactNode } from "react";
     
    export default function RootLayout({ children }: { children: ReactNode }) {
      return (
        <html lang="en" suppressHydrationWarning>
          <body>
            <ThemeProvider attribute="class">{children}</ThemeProvider>
          </body>
        </html>
      );
    }

    suppressHydrationWarning covers only the element's own attributes and text, one level deep, so mismatches inside the page are still reported.

  4. For extensions on <body>, you can add suppressHydrationWarning to <body> to silence them in development. It hides your own <body> attribute differences too, so keep it off <body> if you set attributes there. See when suppressHydrationWarning is safe.

  5. For scripts, load them after hydration so they change the page once React owns it.

Find every instance

hydration-proof reports an attribute that only the page has as HP1005. When it can tell that a script or extension added it before hydration, it reports HP4001 or HP4002 instead:

npx hydration-proof test

The test browser runs without extensions. If you simulate one, or if your own scripts add attributes on purpose, skip those attributes in the config:

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

audit-suppress-hydration-warning reviews every suppressHydrationWarning you add, so none of them hides more than intended. Ignoring findings covers the other options.