Reputation: 39018
I've added NextUI to my NextJS 14 app
The issue has been isolated to the ThemeProvider in my main providers.tsx file:
'use client';
import { NextUIProvider } from '@nextui-org/react';
import { ThemeProvider as NextThemesProvider } from 'next-themes';
export default function Providers({ children }: { children: React.ReactNode }) {
return (
<NextUIProvider>
<NextThemesProvider // <-- the issue
attribute="class"
defaultTheme="dark"
themes={['light', 'dark', 'modern']}
>
{children}
</NextThemesProvider>
</NextUIProvider>
);
}
The problem is this specifically, because if I remove it the error goes away:
<NextThemesProvider
attribute="class"
defaultTheme="dark"
themes={['light', 'dark', 'modern']}
>
The warning
**Warning: Extra attributes from the server: class,style
at html
at RedirectErrorBoundary (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/redirect-boundary.js:73:9)
at RedirectBoundary (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/redirect-boundary.js:81:11)
at NotFoundErrorBoundary (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/not-found-boundary.js:76:9)
at NotFoundBoundary (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/not-found-boundary.js:84:11)
at DevRootNotFoundBoundary (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/dev-root-not-found-boundary.js:33:11)
at ReactDevOverlay (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/react-dev-overlay/internal/ReactDevOverlay.js:84:9)
at HotReload (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/react-dev-overlay/hot-reloader-client.js:307:11)
at Router (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/app-router.js:182:11)
at ErrorBoundaryHandler (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/error-boundary.js:114:9)
at ErrorBoundary (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/error-boundary.js:161:11)
at AppRouter (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/components/app-router.js:538:13)
at ServerRoot (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/app-index.js:129:11)
at RSCComponent
at Root (webpack-internal:///(app-pages-browser)/./node_modules/next/dist/client/app-index.js:145:11)**
When I dive into the ThemeProvider type:
interface ThemeProviderProps {
/** List of all available theme names */
themes?: string[] | undefined;
/** Forced theme name for the current page */
forcedTheme?: string | undefined;
/** Whether to switch between dark and light themes based on prefers-color-scheme */
enableSystem?: boolean | undefined;
/** Disable all CSS transitions when switching themes */
disableTransitionOnChange?: boolean | undefined;
/** Whether to indicate to browsers which color scheme is used (dark or light) for built-in UI like inputs and buttons */
enableColorScheme?: boolean | undefined;
/** Key used to store theme setting in localStorage */
storageKey?: string | undefined;
/** Default theme name (for v0.0.12 and lower the default was light). If `enableSystem` is false, the default theme is light */
defaultTheme?: string | undefined;
/** HTML attribute modified based on the active theme. Accepts `class` and `data-*` (meaning any data attribute, `data-mode`, `data-color`, etc.) */
attribute?: string | 'class' | undefined;
/** Mapping of theme name to HTML attribute value. Object where key is the theme name and value is the attribute value */
value?: ValueObject | undefined;
/** Nonce string to pass to the inline script for CSP headers */
nonce?: string | undefined;
/** React children */
children: React.ReactNode;
}
class
is ok to send into the attribute
prop, is this an issue that React doesn't like?
I've found a few similar issues, but none related to NextUI, any thoughts on how I can remove this warning / error from the console?
I did have a custom ThemeContext I created, but I still get the error warning after I comment it out:
import { FC, ReactNode } from 'react';
import Providers from '@/redux/provider';
import MainHeader from '@/components/mainheader';
import SideBar from '@/components/sidebar';
// import { ThemeProvider } from '@/context/ThemeContext';
import { User } from '@/types/user';
interface layoutProps {
children: ReactNode;
}
// const user: User = {
// theme: 'dark',
// };
const Layout: FC<layoutProps> = ({ children }) => {
return (
// <ThemeProvider user={user}>
<main>
<MainHeader />
<SideBar />
<Providers>{children}</Providers>
</main>
// </ThemeProvider>
);
};
export default Layout;
I also tried adding this suppressHydrationWarning
, but did not work to remove the warning either.
<div suppressHydrationWarning={true}>
<NextUIProvider>
<NextThemesProvider
attribute="class"
defaultTheme="dark"
themes={['light', 'dark', 'modern']}
>
{children}
</NextThemesProvider>
</NextUIProvider>
</div>
Upvotes: 7
Views: 2337
Reputation: 919
Rather than suppressing the warning, you could also lazy load the ThemeProvider as highlighted here https://github.com/pacocoursey/next-themes?tab=readme-ov-file#avoid-hydration-mismatch
Note: Please test the production build, this might throw dynamic server usage error during build and using "force-dynamic" might help to resolve this
// layout.tsx
import dynamicImport from "next/dynamic";
export const dynamic = "force-dynamic"; // this might resolve the dynamic server usage error
const ThemeProvider = dynamicImport(
() => import("@/components/theme-provider").then((mod) => mod.ThemeProvider),
{
ssr: false,
loading: () => (
// Optional: Add skeleton loader here
<div className="min-h-screen bg-background" />
),
}
);
export default function RootLayout({ children }: Readonly<Props>) {
return (
<html lang="en">
<body
className={cn(
"min-h-screen bg-background font-sans antialiased",
inter.variable
)}
>
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
{children}
</ThemeProvider>
</body>
</html>
);
}
Upvotes: 0
Reputation: 1
Finally yeah!
you should create ThemeProvider only on client side I mean only yeah its mean when components mounted
https://github.com/vercel/next.js/discussions/49832#discussioncomment-11038857
"use client";
import { ThemeProvider } from "next-themes";
import Navbar from "./Navbar";
import { useEffect, useState } from "react";
interface Props {
children: React.ReactNode;
}
const BaseLayout = ({ children }: Props) => {
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true); // Only render the ThemeProvider after mounting
}, []);
if (!mounted) {
return <>{children}</>; // Render without theme provider initially (avoids mismatch)
}
return (
<ThemeProvider attribute="class" enableSystem={false}>
<Navbar />
<main className="pt-16">{children}</main>
</ThemeProvider>
);
};
export default BaseLayout;
Upvotes: 0
Reputation: 39018
Placed the suppressHydrationWarning
flag on the wrong div
.
I had to add the flag here on the root layout file:
<html suppressHydrationWarning={true} lang="en">
Warning is gone now.
Upvotes: 2