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>;
}
Add suppressHydrationWarning this is what the official documentation says;
Note! If you do not add
suppressHydrationWarningto your<html>you will get warnings because next-themes updates that element. This property only applies one level deep, so it won't block hydration warnings on other elements.
https://github.com/pacocoursey/next-themes
First Step:
To resolve this, you can conditionally render the ThemeProvider only after the component has mounted on the client side. This approach ensures that the server-side rendering does not conflict with client-side rendering.
"use client";
import { ThemeProvider } from "next-themes";
import { useState, useEffect } from "react";
export default function Providers({ children }: { children: React.ReactNode }) {
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
if (!mounted) {
return <>{children}</>; // Render children without ThemeProvider during SSR
}
return <ThemeProvider attribute="class">{children}</ThemeProvider>; // Wrap children with ThemeProvider after mount
}
Second Step: Update Your Layout Component: Use the Providers component to wrap your main content in layout.tsx.
import type { Metadata } from "next";
import Providers from "./providers";
import "./globals.css";
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body>
<Providers>
{children}
</Providers>
</body>
</html>
);
}
Now you are all set wont see any hydration warning
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With