DEV Community

Cover image for Dark Mode with Tailwind v4 & next-themes
Abu Jakaria
Abu Jakaria

Posted on

Dark Mode with Tailwind v4 & next-themes

A simple guide to implementing dark mode in React using Tailwind CSS v4 and next-themes.


πŸ“¦ Installation

npm install next-themes
Enter fullscreen mode Exit fullscreen mode

πŸš€ Setup

Step 1: Wrap App with ThemeProvider

In main.jsx:

import { ThemeProvider } from "next-themes";

createRoot(document.getElementById("root")).render(
  <StrictMode>
    <ThemeProvider attribute="class" defaultTheme="system" enableSystem>
      <App />
    </ThemeProvider>
  </StrictMode>
);
Enter fullscreen mode Exit fullscreen mode

Props:

  • attribute="class" - Adds dark class to <html> element
  • defaultTheme="system" - Uses system preference by default
  • enableSystem - Detects system theme automatically
  • storageKey="theme-name" - (Optional) Custom localStorage key

Step 2: Configure Colors in index.css

@import "tailwindcss";

@variant dark (&:is(.dark *));

@theme {
  --color-primary: light-dark(#10b981, #dc2626);
  --color-surface: light-dark(#f8fafc, #0f172a);
}
Enter fullscreen mode Exit fullscreen mode

Syntax:

--color-[name]: light-dark([light-color], [dark-color]);
Enter fullscreen mode Exit fullscreen mode

🎨 Using Custom Colors

Define in CSS:

--color-primary: light-dark(#10b981, #dc2626);
Enter fullscreen mode Exit fullscreen mode

Use in JSX:

<h1 className="text-primary">Hello</h1>
<div className="bg-primary">Content</div>
Enter fullscreen mode Exit fullscreen mode

Rule: Don't include text- or bg- in variable names. Tailwind adds them automatically!


πŸ”§ Theme Toggle

Basic Implementation

import { useTheme } from "next-themes";

function ThemeToggle() {
  const { theme, setTheme } = useTheme();

  return (
    <button onClick={() => setTheme(theme === "dark" ? "light" : "dark")}>
      {theme === "dark" ? "πŸŒ™" : "β˜€οΈ"}
    </button>
  );
}
Enter fullscreen mode Exit fullscreen mode

Prevent Hydration Issues

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

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

  useEffect(() => setMounted(true), []);

  if (!mounted) return null;

  return (
    <button onClick={() => setTheme(theme === "dark" ? "light" : "dark")}>
      Toggle Theme
    </button>
  );
}
Enter fullscreen mode Exit fullscreen mode

πŸ“– useTheme Hook API

const { theme, setTheme, systemTheme, themes } = useTheme();
Enter fullscreen mode Exit fullscreen mode
Property Type Description
theme string Current theme: "light", "dark", or "system"
setTheme(name) function Change theme
systemTheme string System preference: "light" or "dark"
themes array Available themes

πŸ’‘ Common Color Schemes

@theme {
  /* Backgrounds */
  --color-surface: light-dark(#f8fafc, #0f172a);
  --color-card: light-dark(#ffffff, #1e293b);

  /* Text */
  --color-primary: light-dark(#1f2937, #f9fafb);
  --color-secondary: light-dark(#6b7280, #9ca3af);

  /* Brand */
  --color-brand: light-dark(#10b981, #34d399);
  --color-accent: light-dark(#3b82f6, #60a5fa);
}
Enter fullscreen mode Exit fullscreen mode

πŸ› Troubleshooting

Dark mode not working?

  1. Check if <html class="dark"> appears in DevTools
  2. Clear localStorage and refresh
  3. Verify @variant dark (&:is(.dark *)); is in CSS

Colors not changing?

  1. Don't include text- or bg- in color variable names
  2. Use correct syntax: light-dark(#fff, #000)

🎯 Quick Reference

// Setup
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>

// Toggle theme
const { theme, setTheme } = useTheme();
setTheme(theme === "dark" ? "light" : "dark");

// Define colors
--color-primary: light-dark(#10b981, #dc2626);

// Use colors
<div className="bg-primary text-secondary">
Enter fullscreen mode Exit fullscreen mode

πŸ“š Resources

Top comments (0)