Implementing Dark Mode Toggle in React

In this tutorial, I'm going to show a simple, reliable, and production-ready way to add dark mode to any React or Next.js app.
What We'll Build:
- A dark mode system that:
- Saves the user’s preference in
localStorage - Applies the theme instantly with no flicker
- Works with Tailwind’s
dark:classes - Is reusable anywhere in your app
1. Configure Tailwind for Dark Mode
In tailwind.config.js:
1module.exports = {
2 darkMode: "class", // <-- Enable class-based dark mode
3 content: ["./src/**/*.{js,ts,jsx,tsx}"],
4 theme: {},
5 plugins: [],
6};
7This tells Tailwind:
"If class="dark" exists on the HTML element, apply dark styles."
2. Add Theme Logic (React Hook + LocalStorage)
Create a hook you can use anywhere:
1import { useEffect, useState } from "react";
2
3export function useTheme() {
4 const [theme, setTheme] = useState("light");
5
6 useEffect(() => {
7 // Load saved theme on mount
8 const saved = localStorage.getItem("theme");
9 if (saved) {
10 setTheme(saved);
11 document.documentElement.classList.toggle("dark", saved === "dark");
12 }
13 }, []);
14
15 useEffect(() => {
16 // Update HTML class & save setting
17 document.documentElement.classList.toggle("dark", theme === "dark");
18 localStorage.setItem("theme", theme);
19 }, [theme]);
20
21 const toggle = () => {
22 setTheme((prev) => (prev === "light" ? "dark" : "light"));
23 };
24
25 return { theme, toggle };
26}
273. Build the Toggle Button
1"use client";
2import { useTheme } from "@/hooks/useTheme";
3
4export default function ThemeToggle() {
5 const { theme, toggle } = useTheme();
6
7 return (
8 <button
9 onClick={toggle}
10 className="p-2 rounded-lg border hover:bg-gray-100 dark:hover:bg-gray-700 transition"
11 >
12 {theme === "light" ? "🌙" : "☀️"}
13 </button>
14 );
15}
164. Use It Anywhere
Add the button to your navbar, header, or footer:
1import ThemeToggle from "@/components/ThemeToggle";
2
3export default function Navbar() {
4 return (
5 <nav className="flex justify-between items-center py-4">
6 <h1 className="text-xl font-bold">My Website</h1>
7 <ThemeToggle />
8 </nav>
9 );
10}
115. Style Your Website With Dark Mode Classes:
Example:
1<div className="bg-white dark:bg-gray-900 text-gray-900 dark:text-gray-100 min-h-screen">
2 <p>Hello world!</p>
3</div>
4Tailwind will automatically switch styles when:
1<html class="dark">
2is present.
6. Prevent Flash of Wrong Theme
Now this is highly recommended. To do this, you have to create a theme-init.js file:
1// Runs before React hydration
2(function () {
3 const saved = localStorage.getItem("theme");
4 if (saved === "dark") {
5 document.documentElement.classList.add("dark");
6 }
7})();
8Then include it inside <head> in Next.js:
1<head>
2 <script src="/theme-init.js" />
3</head>
4This ensures your page loads directly in dark mode if the user prefers it — no flash.
Final Product
You now have:
- A reusable React hook
- A toggle button
- Persistent theme preference
- Tailwind dark-mode support
- No UI flicker
Clean, modern, production-ready.