Search code examples
next.jstailwind-cssdarkmode

Using next-themes for dark mode generates hydration failed error


I am trying to implement a dark mode using next-themes. Inside the layout.tsx file, <body> contains a background color. Hence, I need to wrap the whole <body> inside <ThemeProvider> to change the background as well when toggling to dark mode. I am importing the <ThemeProvider> from ./lib/providers.ts as a client component to avoid making the whole app a client component.

providers.ts file as follows:

"use client";

export { ThemeProvider } from "next-themes";

while the layout.tsx file is as follows:

import Navbar from "@/components/Navbar";
import "./globals.css";
import type { Metadata } from "next";
import { Nunito_Sans } from "next/font/google";
import Sidebar from "@/components/Sidebar";
import { ThemeProvider } from "@/lib/providers";

const nunito_sans = Nunito_Sans({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: "My name | Portfolio",
  description: "My moto",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <ThemeProvider>
        <body
          className={`${nunito_sans.className} bg-gradient-to-r from-green to to-blue-400`}
        >
          <main className="grid grid-cols-12 gap-6 my-14 px-5 lg:px-48 sm:px-20 md:px-32">
            <div className="col-span-12 lg:col-span-3 bg-white rounded-2xl p-4 text-center">
              <Sidebar />
            </div>
            <div className="col-span-12 lg:col-span-9 bg-white rounded-2xl flex flex-col overflow-hidden">
              <Navbar />
              {children}
            </div>
          </main>
        </body>
      </ThemeProvider>
    </html>
  );
}

However, it throughs an error:

❌ Error
Hydration failed because the initial UI does not match what was rendered on the server.
⚠️ Warning
Expected server HTML to contain a matching in .

I have also tried creating a separate component <Provider> and wrapping the body inside it but, the same warning occurs:

"use client";

import { ThemeProvider } from "next-themes";

export default function Providers({ children }: { children: React.ReactNode }) {
  return <ThemeProvider>{children}</ThemeProvider>;
}

Solution

  • Within your layout.tsx, remove <html> and <body> tags.

    If you have a need for <head> tag, use the component <Head> from Next.js like:

    import Head from 'next/head'
    

    So your layout.tsx becomes:

    import Navbar from "@/components/Navbar";
    import "./globals.css";
    import type { Metadata } from "next";
    import { Nunito_Sans } from "next/font/google";
    import Sidebar from "@/components/Sidebar";
    import { ThemeProvider } from "@/lib/providers";
    
    const nunito_sans = Nunito_Sans({ subsets: ["latin"] });
    
    export const metadata: Metadata = {
      title: "My name | Portfolio",
      description: "My moto",
    };
    
    export default function RootLayout({
      children,
    }: {
      children: React.ReactNode;
    }) {
      return (
        <ThemeProvider>
          <main className={`${nunito_sans.className} bg-gradient-to-r from-green to to-blue-400 grid grid-cols-12 gap-6 my-14 px-5 lg:px-48 sm:px-20 md:px-32`}>
            <div className="col-span-12 lg:col-span-3 bg-white rounded-2xl p-4 text-center">
              <Sidebar />
            </div>
            <div className="col-span-12 lg:col-span-9 bg-white rounded-2xl flex flex-col overflow-hidden">
              <Navbar />
              {children}
            </div>
          </main>
        </ThemeProvider>
      );
    }
    

    You also need to verify that within Sidebar and Navbar components, you have only valid html according to W3C (failing which you shall hydration error from Next.js).