Trouble Implementing Dark Mode in Next.js Project Using App Directory #2648
-
Hi there! I'm currently working on adding dark mode functionality to my Next.js project using the new app directory structure, and I've run into a bit of a snag. Despite the toggle button for switching themes registering click events (as shown in my console logs), the theme doesn't seem to change across my components as expected. Here's a brief overview of how I've set things up:
import * as React from "react"
import { Moon, Sun } from "lucide-react"
import { useTheme } from "next-themes"
import { Button } from "@/components/ui/button"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
export function ModeToggle() {
const { setTheme } = useTheme()
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="icon">
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
<span className="sr-only">Toggle theme</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => { setTheme("light"); console.log("Theme set to light"); }}>
Light
</DropdownMenuItem>
<DropdownMenuItem onClick={() => { setTheme("dark"); console.log("Theme set to dark"); }}>
Dark
</DropdownMenuItem>
<DropdownMenuItem onClick={() => { setTheme("system"); console.log("Theme set to system preference"); }}>
System
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
}
import "./globals.css";
import { getSessionStatus } from "./actions/AuthActions/isUserLoggedIn";
import ClientLayout from "./components/ClientLayout";
import { Inter as FontSans } from "next/font/google";
import { cn } from "@/lib/utils";
import { ThemeProvider } from "../components/theme-provider";
const fontSans = FontSans({
subsets: ['latin'],
variable: "--font-sans",
})
export default async function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
const isLoggedIn = await getSessionStatus();
return (
<html lang="en">
<body
className={cn(
"min-h-screen bg-background font-sans antialiased",
fontSans.variable
)}
>
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
></ThemeProvider>
<div>
<ClientLayout isLoggedIn={isLoggedIn}></ClientLayout>
{children}
</div>
</body>
</html>
);
}
"use client";
import Image from "next/image";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { ModeToggle } from "./shadcnUIDarkMode/botonDarkMode";
export default function ClientLayout({ isLoggedIn }: { isLoggedIn: boolean }) {
const router = useRouter();
const signOut = async () => {
try {
const response = await fetch("/auth/sign-out", {
method: "POST",
});
if (response.ok) {
router.push("/");
router.refresh();
console.log("The resource has been permanently moved.");
}
} catch (error: unknown) {
console.error("An error occurred:", error);
}
};
const logIn = () => {
router.push("/iniciar-sesion");
};
const signup = () => {
router.push("/crear-cuenta");
};
return (
<nav className="flex items-center justify-between flex-wrap bg-teal-500 p-6">
<div className="flex items-center flex-shrink-0 text-white mr-6">
{/* ... logo code ... */}
</div>
<div className="block lg:hidden">{/* ... button code ... */}</div>
<div className="w-full block flex-grow lg:flex lg:items-center lg:w-auto">
<div className="text-sm lg:flex-grow flex items-center">
<Link href="/">
<div className="cursor-pointer flex items-center mr-2">
<Image
src="/feed-the-pig.svg"
alt="Home"
width={34}
height={34}
/>
<span className="text-teal-200 hover:text-white ml-2">
Inicio
</span>
</div>
</Link>
<Link href="/ingresos" passHref>
<span className="block mt-4 lg:inline-block lg:mt-0 text-teal-200 hover:text-white mr-4">
Ingresos
</span>
</Link>
<Link href="/gastos" passHref>
<span className="block mt-4 lg:inline-block lg:mt-0 text-teal-200 hover:text-white">
Gastos
</span>
</Link>
<ModeToggle></ModeToggle>
</div>
<div>
{isLoggedIn ? (
<button
onClick={signOut}
className="inline-block text-sm px-4 py-2 leading-none border rounded text-white border-white hover:border-transparent hover:text-teal-500 hover:bg-white lg:mt-0"
>
Cerrar sesion
</button>
) : (
<>
<button
onClick={logIn}
className="inline-block text-sm px-4 py-2 leading-none border rounded text-white border-white hover:border-transparent hover:text-teal-500 hover:bg-white lg:mt-0 mr-2"
>
Iniciar Sesion
</button>
<button
onClick={signup}
className="inline-block text-sm px-4 py-2 leading-none border rounded text-white border-white hover:border-transparent hover:text-teal-500 hover:bg-white lg:mt-0"
>
Crear Cuenta
</button>
</>
)}
</div>
</div>
</nav>
);
} Demonstration Video: [Link to the video] - This video showcases the issue at hand, where clicking the toggle button logs the theme change in the console but doesn't visually update the UI components as expected. localhost_3000_ingresos.-.Personal_.Microsoft.Edge.2024-02-01.23-19-50.mp4I've double-checked my implementation against the documentation and various tutorials, but I can't seem to pinpoint where the issue lies. Any insights, suggestions, or guidance on what might be going wrong would be greatly appreciated! Thank you in advance for your help! |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 3 replies
-
First thing is you should wrap 'use client';
import * as React from "react"
import { Moon, Sun } from "lucide-react"
import { useTheme } from "next-themes"
import { Button } from "@/components/ui/button"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
export function ModeToggle() {
const { setTheme } = useTheme()
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="icon">
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
<span className="sr-only">Toggle theme</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => { setTheme("light"); console.log("Theme set to light"); }}>
Light
</DropdownMenuItem>
<DropdownMenuItem onClick={() => { setTheme("dark"); console.log("Theme set to dark"); }}>
Dark
</DropdownMenuItem>
<DropdownMenuItem onClick={() => { setTheme("system"); console.log("Theme set to system preference"); }}>
System
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
} Also you should add import "./globals.css";
import { getSessionStatus } from "./actions/AuthActions/isUserLoggedIn";
import ClientLayout from "./components/ClientLayout";
import { Inter as FontSans } from "next/font/google";
import { cn } from "@/lib/utils";
import { ThemeProvider } from "../components/theme-provider";
const fontSans = FontSans({
subsets: ['latin'],
variable: "--font-sans",
})
export default async function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
const isLoggedIn = await getSessionStatus();
return (
<html lang="en" suppressHydrationWarning>
<body
className={cn(
"min-h-screen bg-background font-sans antialiased",
fontSans.variable
)}
>
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
></ThemeProvider>
<div>
<ClientLayout isLoggedIn={isLoggedIn}></ClientLayout>
{children}
</div>
</body>
</html>
);
} I hope this will resolve the issue you are facing. If you feel any other issues please feel free to ask here |
Beta Was this translation helpful? Give feedback.
I was not wrapping correctly in the layout the {children}
Here is how the correct code looks