This React hydration warning occurs when CSS-in-JS libraries inject style tags during server-side rendering that React does not expect during client-side hydration, causing a mismatch between server and client HTML.
This warning is a hydration error that appears when React detects style tags in the server-rendered HTML that were not expected during client-side hydration. Hydration is the process where React attaches event handlers to server-rendered HTML, and it expects the server and client markup to be identical. The error typically occurs with CSS-in-JS solutions like styled-components, Emotion, or other libraries that inject styles dynamically. These libraries often render styles as a side effect of component rendering, meaning React needs to fully render the page to determine which components are used and what styles they need. When the server renders styles in one way but the client expects them differently, React detects this mismatch and issues a warning. While this warning does not break your application, it indicates that React had to do extra work to reconcile the differences between server and client markup, which can impact performance and may lead to visual flashes or incorrect initial renders.
Check if you have properly configured server-side rendering for your CSS-in-JS library. For styled-components with Next.js, create a custom _document.js:
// pages/_document.js
import Document, { Html, Head, Main, NextScript } from 'next/document';
import { ServerStyleSheet } from 'styled-components';
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const sheet = new ServerStyleSheet();
const originalRenderPage = ctx.renderPage;
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) =>
sheet.collectStyles(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
),
};
} finally {
sheet.seal();
}
}
render() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}This ensures styled-components collects styles during server rendering and injects them properly.
The Babel plugin ensures deterministic class names and prevents checksum mismatches. Install it:
npm install --save-dev babel-plugin-styled-componentsThen add to your .babelrc or babel.config.js:
{
"presets": ["next/babel"],
"plugins": [["styled-components", { "ssr": true }]]
}For Next.js 12+, you can use the built-in compiler option in next.config.js:
module.exports = {
compiler: {
styledComponents: true,
},
};If using Emotion, set up the cache provider and extract critical styles. Create an Emotion cache:
// lib/createEmotionCache.js
import createCache from '@emotion/cache';
export default function createEmotionCache() {
return createCache({ key: 'css', prepend: true });
}In your _app.js (Next.js) or root component:
import { CacheProvider } from '@emotion/react';
import createEmotionCache from '../lib/createEmotionCache';
const clientSideEmotionCache = createEmotionCache();
function MyApp({ Component, emotionCache = clientSideEmotionCache, pageProps }) {
return (
<CacheProvider value={emotionCache}>
<Component {...pageProps} />
</CacheProvider>
);
}In your custom _document.js, extract and inject critical styles:
import Document, { Html, Head, Main, NextScript } from 'next/document';
import createEmotionServer from '@emotion/server/create-instance';
import createEmotionCache from '../lib/createEmotionCache';
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const originalRenderPage = ctx.renderPage;
const cache = createEmotionCache();
const { extractCriticalToChunks } = createEmotionServer(cache);
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) => (
<App emotionCache={cache} {...props} />
),
});
const initialProps = await Document.getInitialProps(ctx);
const emotionStyles = extractCriticalToChunks(initialProps.html);
const emotionStyleTags = emotionStyles.styles.map((style) => (
<style
data-emotion={`${style.key} ${style.ids.join(' ')}`}
key={style.key}
dangerouslySetInnerHTML={{ __html: style.css }}
/>
));
return {
...initialProps,
emotionStyleTags,
};
}
render() {
return (
<Html>
<Head>{this.props.emotionStyleTags}</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}If hydration warnings persist and are difficult to resolve, consider migrating to styling solutions that work better with SSR:
Tailwind CSS:
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -pCSS Modules (built into Next.js):
import styles from './Component.module.css';
export default function Component() {
return <div className={styles.container}>Content</div>;
}Both solutions generate static CSS that does not cause hydration mismatches.
Ensure your CSS-in-JS library injects styles in the <head> tag, not in the <body>. Check your configuration:
// For Emotion, use prepend: true in cache config
const cache = createCache({ key: 'css', prepend: true });
// For styled-components, styles should automatically go in <head>
// If they don't, check your ServerStyleSheet implementationInspect your rendered HTML to confirm style tags appear in <head> on both server and client.
After making configuration changes, clear the Next.js cache and rebuild:
rm -rf .next
npm run build
npm run startFor development, restart your dev server:
npm run devConfiguration changes to Babel plugins or Next.js compiler options require a full restart.
For frameworks using streaming SSR (like React 18's Suspense), CSS-in-JS solutions may require double-rendering: the first render extracts all necessary styles, and the second render streams to the browser with those styles included. This can impact server performance.
If using React Server Components in Next.js 13+ App Router, CSS-in-JS libraries that rely on runtime JavaScript may not work in server components. Stick to CSS Modules, Tailwind, or other zero-runtime solutions for server components, and only use CSS-in-JS in Client Components (marked with "use client").
The suppressHydrationWarning={true} attribute can silence this warning on specific elements, but it's an escape hatch that should be used sparingly. It doesn't fix the underlying issue and may hide real problems.
For production builds, these warnings are typically not shown to users as React's development checks are disabled. However, the underlying hydration mismatch still causes React to do extra work, potentially impacting initial render performance.
React Hook useCallback has a missing dependency: 'variable'. Either include it or remove the dependency array react-hooks/exhaustive-deps
React Hook useCallback has a missing dependency
Cannot use private fields in class components without TS support
Cannot use private fields in class components without TS support
Cannot destructure property 'xxx' of 'undefined'
Cannot destructure property of undefined when accessing props
useNavigate() may be used only in the context of a <Router> component.
useNavigate() may be used only in the context of a Router component
Cannot find module or its corresponding type declarations
How to fix "Cannot find module or type declarations" in Vite