Implementing Dark Mode Toggle in React

·Tutorial · 1 mins
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};
7

This 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}
27

3. 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}
16

4. 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}
11

5. 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>
4

Tailwind will automatically switch styles when:

1<html class="dark">
2

is 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})();
8

Then include it inside <head> in Next.js:

1<head>
2  <script src="/theme-init.js" />
3</head>
4

This 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.