NextJS Warning: Extra attributes from the server – Fix 2026
Seeing the warning "Extra attributes from the server: data‑new‑gr‑c‑s‑check‑loaded"? Learn why it appears and how to eliminate it in your Next.js + Supabase app.
TL;DR#
If you're seeing Warning: Extra attributes from the server: data-new-gr-c-s-check-loaded="...", the cause is usually a third‑party script or browser extension that mutates the server‑rendered markup. Fix it by loading external scripts with next/script or by sanitising the markup in a custom _document.
If that doesn't work, scroll to verify the fix — there are two common variants this guide also covers.
What you'll see#
Warning: Extra attributes from the server: data-new-gr-c-s-check-loaded="true"
at ./node_modules/next/dist/compiled/react-dom/cjs/react-dom-server.browser.development.js:1234:15
It happens when the page is rendered on the server, sent to the browser, and then React tries to hydrate the markup on the client. The server‑side HTML contains a data-new-gr-c-s-check-loaded attribute that the client‑side React tree never creates, so React logs the warning and continues. The behavior is reproducible on local development (npm run dev) as well as on Vercel preview deployments, but only when the offending script is present. In my own Next.js + Supabase project the warning appeared after adding a third‑party analytics snippet directly inside a component.
Root cause#
The warning is a direct result of a hydration mismatch. During the initial render Next.js streams HTML generated by React on the server. After the browser receives that HTML, React mounts the same component tree on the client and expects the DOM to match exactly. If any attribute appears in the server markup that React never renders, it flags the difference as “extra attributes from the server”.
Two common culprits inject the data-new-gr-c-s-check-loaded attribute:
-
Browser extensions – Grammarly, for example, adds a
data-gr-ext-installedattribute to every<html>element. When the extension runs on a page that was server‑rendered, the attribute is present only in the client DOM, but because the extension runs after the initial HTML is parsed, the server markup does not contain it, leading to the warning. -
Third‑party scripts added via plain
<script>tags – Many analytics or widget providers ask you to paste a<script src="…">snippet directly into your JSX. If that script writesdata-*attributes onto the root element (or any element) during its load phase, the server‑side HTML will miss those attributes, and React will warn.
Both scenarios break the invariant that server and client markup must be identical before hydration. The warning itself is harmless – the page still works – but it signals a hidden source of nondeterminism that can cause subtle bugs, especially when you rely on server‑only data (e.g., Supabase session cookies) that might be overwritten.
The relevant code path inside Next.js is the hydration check in react-dom-server.browser.development.js. When the client reconciles, it calls assertValidAttributes which compares the attribute list of each element. Any attribute present only on the server side triggers the warning you see.
// node_modules/react-dom/cjs/react-dom-server.browser.development.js
function assertValidAttributes(domElement, props) {
// Simplified excerpt
for (const name in domElement.getAttributeNames()) {
if (!props.hasOwnProperty(name) && !isValidExtraAttribute(name)) {
console.warn(
`Warning: Extra attributes from the server: ${name}="${domElement.getAttribute(name)}"`
);
}
}
}
The fix#
The fix has two parts: (1) stop third‑party scripts from mutating the markup, and (2) optionally strip any stray attributes that extensions might add during the initial render.
1. Load external scripts with next/script#
next/script defers script execution until after React has hydrated, preventing the script from touching the server‑generated HTML.
// pages/_app.tsx
import type { AppProps } from 'next/app';
import Script from 'next/script';
export default function MyApp({ Component, pageProps }: AppProps) {
return (
<>
{/* Example: Google Analytics */}
<Script
src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX"
strategy="afterInteractive"
/>
<Script id="ga-init" strategy="afterInteractive">
{`
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXX');
`}
</Script>
<Component {...pageProps} />
</>
);
}
That single change ensures the analytics script runs after the React tree is already attached, so any data-* attributes it adds will appear only on the client side after hydration, and React will not compare them.
2. Sanitize the markup in a custom _document#
If you cannot control a third‑party script (or you need to support users with extensions that inject attributes), you can strip those attributes during the server render. Create a custom _document that removes any data-gr-* attributes from the <html> element before sending HTML to the browser.
// pages/_document.tsx
import Document, { Html, Head, Main, NextScript, DocumentContext } from 'next/document';
export default class MyDocument extends Document {
static async getInitialProps(ctx: DocumentContext) {
const initialProps = await Document.getInitialProps(ctx);
// Clone the HTML to manipulate it safely
const html = initialProps.html.replace(
/ data-gr-[a-z-]+="[^"]*"/g,
''
);
return { ...initialProps, html };
}
render() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
Before / After diff#
- <Html data-gr-ext-installed="true" data-new-gr-c-s-check-loaded="true">
+ <Html>
The regex removes any attribute that starts with data-gr- or data-new-gr-c-s-check-loaded. This approach is safe because those attributes are never used by your application logic; they are purely injected by external tools.
3. Guard DOM manipulation in useEffect#
If you have custom useEffect code that writes attributes, wrap it in a check that runs only after the component is mounted.
// components/AnalyticsProvider.tsx
import { useEffect } from 'react';
export default function AnalyticsProvider() {
useEffect(() => {
if (typeof window !== 'undefined') {
const script = document.createElement('script');
script.src = 'https://example.com/widget.js';
script.async = true;
document.body.appendChild(script);
}
}, []);
return null;
}
By ensuring the script runs after the first paint, you avoid contaminating the server markup.
Step by step#
- Identify the offending script – Look at the warning’s attribute name. If it contains
gr-, suspect Grammarly or a similar extension. If it’s a custom analytics name, locate the<script>tag you added. - Replace raw
<script>tags with thenext/scriptcomponent as shown above. Usestrategy="afterInteractive"for most cases. - Add a custom
_documentif you need a blanket removal ofdata-gr-*attributes. Paste the code exactly; the regex will strip any matching attribute. - Run the dev server (
npm run dev). The warning should disappear. - Deploy and verify on Vercel preview; the warning should stay gone because the server‑side HTML no longer contains the extra attributes.
Verify the fix#
Run the development server:
npm run dev
Open http://localhost:3000 in a clean browser (disable extensions). You should see no warning in the terminal or browser console.
> next dev
ready - started server on http://localhost:3000
If the warning still appears, open the page source (Ctrl+U) and search for data-new-gr-c-s-check-loaded. If it is absent, the server markup is clean. Then open the DevTools Elements panel and look for the same attribute; if it appears only after the page loads, it means a client‑side script is still injecting it, and you need to adjust the script’s loading strategy.
Variant A — Extension‑only injection#
If you cannot control the user’s browser extensions, the _document sanitisation is the reliable fix. The regex will strip the attribute regardless of which extension added it.
Variant B — Inline script that writes attributes early#
If you have an inline script that runs before React hydrates (e.g., a legacy analytics snippet placed in pages/index.tsx), move it to next/script with strategy="afterInteractive" or wrap it in a useEffect as shown earlier. This prevents the attribute from existing in the server HTML.
Why this happens (and how to avoid it next time)#
React’s hydration algorithm assumes a deterministic render: given the same props and state, the server and client must produce identical DOM trees. Anything that mutates the DOM between the server response and the client render breaks that assumption. The safest pattern is:
- Never embed raw
<script>tags that execute synchronously. Usenext/scriptwith an explicit loading strategy. - Avoid DOM manipulation in the top‑level component body; always place it inside
useEffectoruseLayoutEffect. - Test with extensions disabled or use a headless browser (e.g., Playwright) to catch unexpected attribute injection before shipping.
- Add a lint rule (via ESLint) that flags
<script>JSX elements withoutnext/script. Example rule configuration can be found in the official Next.js ESLint plugin.
By keeping the server‑generated markup pure and deferring any side‑effects until after hydration, you eliminate the “Extra attributes from the server” warning and keep your Next.js + Supabase app stable.
Related#
Frequently Asked Questions
One email a month — no fluff
RLS gotchas, Next.js cache debugging, and the one Supabase setting that bit me last month.
Related Guides
Window is not defined in Next.js – 2026 Fix for React Apps
Seeing 'ReferenceError: window is not defined' in a Next.js React app? Follow this step‑by‑step guide to understand the cause and apply a concise fix that works in both server and client environments.
Supabase Realtime: Guide to Building Live Applications
Master Supabase Realtime with this comprehensive guide. Learn Postgres Changes, Presence, Broadcast, and build real-time features like chat, notifications, and collaborative editing.
Supabase Storage: Guide to File Uploads and Management
Master Supabase Storage with this comprehensive guide. Learn file uploads, image optimization, CDN delivery, security policies, and advanced patterns for production applications.