Dark mode is a must-have feature in modern web apps. In this guide, you'll learn how to implement a fully functional, accessible, and SSR-friendly dark mode toggle in your Next.js app using the next-themes package and TailwindCSS.

Let’s get started!

step 1 β†’ Create a Next.js App

npx create-next-app@latest
πŸ“ my-next-app/
β”œβ”€β”€ app/
β”‚   β”œβ”€β”€ layout.tsx
β”‚   └── ThemeSwitcher.tsx
β”œβ”€β”€ components/
β”‚   └── ThemeProvider.tsx
β”œβ”€β”€ pages/ (if applicable)
β”œβ”€β”€ public/
β”œβ”€β”€ styles/
β”œβ”€β”€ ...

step 2 β†’ install next-themes dependency

npm install next-themes
# or
pnpm add next-themes
# or
yarn add next-themes

step 3 β†’ create components/ThemeProvider.tsx

// ThemeProvider.tsx
'use client'

import { ThemeProvider as NextThemeProvider } from 'next-themes'
import { type ThemeProviderProps } from 'next-themes'

// Wraps the entire app with next-themes provider
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
  return <NextThemeProvider {...props}>{children}</NextThemeProvider>
}

step 4 β†’ modify the app/layout.tsx

   <html lang="en" suppressHydrationWarning>
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased`} 
        >
        <ThemeProvider
          attribute="class"
          defaultTheme="light"
          enableSystem={false}
          disableTransitionOnChange
        >
          <div className="relative flex min-h-screen w-full ">
            <div
              className={cn(
                "fixed inset-0 z-0",
                "bg-white dark:bg-black",
              )}
            />
            
            <div className="relative z-10 w-full text-gray-900 dark:text-gray-100">
              {children}
            </div>
          </div>
        </ThemeProvider>
      </body>
    </html>

Key Configuration: