ThemeProvider from NextUI causing Warning: Extra attributes from the server: class,style
Asked Answered
N

2

6

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>
Natividadnativism answered 28/3 at 14:50 Comment(0)
N
1

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.

Natividadnativism answered 28/3 at 19:10 Comment(0)
R
0

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;
Rayshell answered 24/10 at 9:33 Comment(1)
While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - From ReviewCapriccio

© 2022 - 2024 — McMap. All rights reserved.