Getting Started
Dark Mode
Dark mode swaps token values under a .dark class. Components do not change.
Theme provider#
Use next-themes to set the class on <html> and remember the choice.
components/theme-provider.tsx
"use client"import { ThemeProvider as NextThemesProvider } from "next-themes"export function ThemeProvider(props: React.ComponentProps<typeof NextThemesProvider>) { return ( <NextThemesProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange {...props} /> )}Wrap your layout#
Add suppressHydrationWarning to <html>, because the class is set before React hydrates.
app/layout.tsx
export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html lang="en" suppressHydrationWarning> <body> <ThemeProvider>{children}</ThemeProvider> </body> </html> )}Add a toggle#
The header of this site uses this toggle. Press D anywhere to switch theme.
"use client"import { useTheme } from "next-themes"import { MoonIcon, SunIcon } from "lucide-react"import { Button } from "@/components/ui/button"export function ThemeToggle() { const { resolvedTheme, setTheme } = useTheme() return ( <Button variant="ghost" size="icon-sm" aria-label="Toggle theme" onClick={() => setTheme(resolvedTheme === "dark" ? "light" : "dark")} > <SunIcon className="dark:hidden" /> <MoonIcon className="hidden dark:block" /> </Button> )}