Skip to content

Building a Dark Mode Toggle with Next.js and Tailwind CSS

Published March 23, 2025
3 min read

Building a Dark Mode Toggle with Next.js and Tailwind CSS

Dark mode has become a standard feature for modern web applications. In this post, we'll build a dark mode toggle using Next.js and Tailwind CSS.

Setting Up Tailwind for Dark Mode

First, configure Tailwind CSS to support dark mode in your tailwind.config.js file:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  darkMode: "class", // Use class strategy instead of media queries
  theme: {
    extend: {},
  },
  plugins: [],
};

Using "class" for darkMode means we'll toggle classes to switch between themes rather than relying on the user's system preferences (though we can still respect those initially).

Theme Provider with next-themes

The next-themes package makes theme management in Next.js simple. Let's install it:

npm install next-themes

Now, create a ThemeProvider component:

// src/providers/theme-provider.tsx
"use client";

import { ThemeProvider as NextThemesProvider } from "next-themes";
import { type ThemeProviderProps } from "next-themes/dist/types";

export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
  return (
    <NextThemesProvider {...props}>
      {children}
    </NextThemesProvider>
  );
}

Wrap your app with this provider in the root layout:

// src/app/layout.tsx
import { ThemeProvider } from "@/providers/theme-provider";

export default function RootLayout({ children }) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body>
        <ThemeProvider
          attribute="class"
          defaultTheme="system"
          enableSystem
          disableTransitionOnChange
        >
          {children}
        </ThemeProvider>
      </body>
    </html>
  );
}

Creating the Toggle Component

Now, let's create the actual toggle button:

// src/components/ui/theme-toggle.tsx
"use client";

import { useTheme } from "next-themes";
import { useEffect, useState } from "react";

export function ThemeToggle() {
  const { theme, setTheme } = useTheme();
  const [mounted, setMounted] = useState(false);

  // Avoid hydration mismatch
  useEffect(() => {
    setMounted(true);
  }, []);

  if (!mounted) {
    return null;
  }

  return (
    <button
      onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
      className="p-2 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800"
    >
      {theme === "dark" ? (
        <SunIcon className="h-5 w-5" />
      ) : (
        <MoonIcon className="h-5 w-5" />
      )}
    </button>
  );
}

function SunIcon(props) {
  return (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      fill="none"
      viewBox="0 0 24 24"
      strokeWidth={1.5}
      stroke="currentColor"
      {...props}
    >
      <path
        strokeLinecap="round"
        strokeLinejoin="round"
        d="M12 3v2.25m6.364.386-1.591 1.591M21 12h-2.25m-.386 6.364-1.591-1.591M12 18.75V21m-4.773-4.227-1.591 1.591M5.25 12H3m4.227-4.773L5.636 5.636M15.75 12a3.75 3.75 0 11-7.5 0 3.75 3.75 0 017.5 0z"
      />
    </svg>
  );
}

function MoonIcon(props) {
  return (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      fill="none"
      viewBox="0 0 24 24"
      strokeWidth={1.5}
      stroke="currentColor"
      {...props}
    >
      <path
        strokeLinecap="round"
        strokeLinejoin="round"
        d="M21.752 15.002A9.718 9.718 0 0118 15.75c-5.385 0-9.75-4.365-9.75-9.75 0-1.33.266-2.597.748-3.752A9.753 9.753 0 003 11.25C3 16.635 7.365 21 12.75 21a9.753 9.753 0 009.002-5.998z"
      />
    </svg>
  );
}

Using the Toggle

You can now place this toggle anywhere in your application:

import { ThemeToggle } from "@/components/ui/theme-toggle";

export function Navbar() {
  return (
    <header className="border-b border-gray-200 dark:border-gray-800">
      <div className="container flex justify-between items-center">
        <div>My Site</div>
        <ThemeToggle />
      </div>
    </header>
  );
}

Styling for Dark Mode

With Tailwind, you can easily add dark mode variants to your styles:

<div className="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100">
  This text adapts to dark mode!
</div>

Conclusion

Implementing dark mode in Next.js with Tailwind CSS is straightforward thanks to libraries like next-themes. The key components are:

  1. Configuring Tailwind for dark mode
  2. Setting up a theme provider
  3. Creating a toggle component
  4. Using dark mode variants in your styles

Try implementing this in your Next.js project and watch your users enjoy the option to browse in their preferred theme!

Share this article

Related Posts

Author

BytescapeHub Team

Tech enthusiasts writing about gaming, AI, and homelab projects. We share insights, tutorials, and the latest technology news.

Subscribe to our newsletter

Get the latest posts delivered right to your inbox.

Comments

Comments are currently disabled. Check back soon!