A simple guide to implementing dark mode in React using Tailwind CSS v4 and next-themes.
π¦ Installation
npm install next-themes
π 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>
);
Props:
-
attribute="class"- Addsdarkclass 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);
}
Syntax:
--color-[name]: light-dark([light-color], [dark-color]);
π¨ Using Custom Colors
Define in CSS:
--color-primary: light-dark(#10b981, #dc2626);
Use in JSX:
<h1 className="text-primary">Hello</h1>
<div className="bg-primary">Content</div>
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>
);
}
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>
);
}
π useTheme Hook API
const { theme, setTheme, systemTheme, themes } = useTheme();
| 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);
}
π Troubleshooting
Dark mode not working?
- Check if
<html class="dark">appears in DevTools - Clear localStorage and refresh
- Verify
@variant dark (&:is(.dark *));is in CSS
Colors not changing?
- Don't include
text-orbg-in color variable names - 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">
Top comments (0)