Dark Mode
Utilities
Registry Architecture
Follow these steps to add light and dark theme switching to your Next.js App Router site. Jolocky footers keep their own colors unless you override them; see Dark Mode for how to do that once your site has a .dark class.
1. Install next-themes#
Install the next-themes package:
pnpm add next-themes
2. Create the Theme Provider#
Create a client-side wrapper component around NextThemesProvider:
"use client"
import * as React from "react"
import { ThemeProvider as NextThemesProvider } from "next-themes"
export function ThemeProvider({
children,
...props
}: React.ComponentProps<typeof NextThemesProvider>) {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>
}3. Wrap Your Root Layout#
Add the ThemeProvider inside your root layout and add suppressHydrationWarning to the html tag to prevent hydration warnings from browser theme detection:
import { ThemeProvider } from "@/components/theme-provider"
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en" suppressHydrationWarning>
<head />
<body>
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
{children}
</ThemeProvider>
</body>
</html>
)
}4. Add a Mode Toggle Button#
Create a toggle button using Lucide icons and the useTheme hook. If your project does not have lucide-react yet, install it with npm install lucide-react.
"use client"
import * as React from "react"
import { Moon, Sun } from "lucide-react"
import { useTheme } from "next-themes"
export function ModeToggle() {
const { theme, setTheme } = useTheme()
return (
<button
onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
className="inline-flex h-9 w-9 items-center justify-center rounded-md border border-border bg-background text-foreground transition-colors hover:bg-accent"
aria-label="Toggle theme"
>
<Sun className="h-4 w-4 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Moon className="absolute h-4 w-4 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
</button>
)
}