DEV Community

Cover image for Dark mode using Tailwindcss v4.0
TenE
TenE

Posted on • Edited on

7

Dark mode using Tailwindcss v4.0

Dark Mode Toggle in React

Dark mode is a theme selector that enhances user engagement by respecting their preferred theme.

Overview

In this blog, we will cover how to toggle between two themes: light and dark. (Multi-theme selection will be covered in upcoming blogs.)

UI Component

We will create a reusable UI component, a button that can be placed anywhere in the application while maintaining the same functionality.

Toggle Button UI

<div
  className="relative w-14 h-8 rounded-3xl bg-slate-300 dark:bg-zinc-700 place-center cursor-pointer"
>
  <div
    className="absolute top-1 rounded-full w-6 h-6 bg-blue-500"
  />
</div>
Enter fullscreen mode Exit fullscreen mode

In global.css or index.css

The following styles help integrate Tailwind CSS and define a custom variant for dark mode:

@import 'tailwindcss';

/* Define a custom variant for dark mode */
@custom-variant dark (&:is(.dark *));
Enter fullscreen mode Exit fullscreen mode
  • @import 'tailwindcss'; Loads Tailwind CSS into your global styles.
  • @custom-variant dark (&:is(.dark *));
    • Creates a custom Tailwind variant named dark.
    • Uses :is(.dark *) to apply styles when any parent element has the .dark class.
    • Ensures that child elements automatically inherit dark mode styles.

Creating a Custom Hook for Toggling Dark Mode

We will now create a custom hook, useDarkMode, to handle dark mode toggling.

Custom Hook: useDarkMode

import { useState, useEffect, useCallback } from "react";

export default function useDarkMode() {
  const [isDarkMode, setIsDarkMode] = useState<boolean>(() => {
    const savedTheme = localStorage.getItem("theme");
    if (savedTheme) {
      return savedTheme === "dark";
    }
    return window.matchMedia("(prefers-color-scheme: dark)").matches;
  });

  // Toggle dark mode
  const toggleDarkMode = useCallback(() => {
    setIsDarkMode((prev) => {
      const newMode = !prev;
      const html = document.documentElement;
      if (newMode) {
        html.classList.add("dark");
        localStorage.setItem("theme", "dark");
      } else {
        html.classList.remove("dark");
        localStorage.setItem("theme", "light");
      }
      return newMode;
    });
  }, []);

  // Sync the theme with the <html> element
  useEffect(() => {
    const html = document.documentElement;
    if (isDarkMode) {
      html.classList.add("dark");
    } else {
      html.classList.remove("dark");
    }
  }, [isDarkMode]);

  return { isDarkMode, toggleDarkMode };
}
Enter fullscreen mode Exit fullscreen mode

Explanation of useDarkMode

  • State Initialization: It first checks localStorage for a saved theme preference. If none exists, it defaults to the system theme preference.
  • Toggling Theme: The toggleDarkMode function switches the theme, updates the html class, and stores the new theme in localStorage.
  • Effect Hook: Ensures the theme remains synced with the html element when the component renders.

Using the Hook in a Component

Now, we will use useDarkMode in a component to toggle dark mode.

ToggleDarkMode Component

import useDarkMode from "../../../hooks/useDarkMode";

export default function ToggleDarkMode() {
  const { toggleDarkMode, isDarkMode } = useDarkMode();

  return (
    <div
      onClick={toggleDarkMode}
      className="relative w-14 h-8 rounded-3xl bg-slate-300 dark:bg-zinc-700 place--center cursor-pointer"
    >
      <div
        className={`absolute top-1 rounded-full w-6 h-6 bg-blue-500 ${isDarkMode ? "left-1" : "right-1"}`}
      />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Explanation

  • Imports useDarkMode Hook: Extracts toggleDarkMode and isDarkMode from the hook.
  • UI Structure: A switch-like button that changes its position based on isDarkMode.
  • Click Handler: Calls toggleDarkMode to switch themes.

Using in Your App

Now you can import and use <ToggleDarkMode /> anywhere in your application!

Example: Using It in a Navbar

import ToggleDarkMode from "./components/ToggleDarkMode";

export default function Navbar() {
  return (
    <nav className="flex justify-between items-center p-4 bg-white dark:bg-black">
      <h1 className="text-xl font-bold text-gray-900 dark:text-white">My Website</h1>
      <ToggleDarkMode />
    </nav>
  );
}
Enter fullscreen mode Exit fullscreen mode

Demo

Check out the live demo: Dark Mode Toggle

Runner H image

Ask Once. Get a Day Trip, Booked & Budgeted.

Want a kid-friendly Paris itinerary with a €100 limit? Runner H books, maps, plans, and syncs it all. Works with Google Maps, Airbnb, Docs & more.

Try Runner H

Top comments (4)

Collapse
 
sreeharshrajan profile image
Sreeharsh K

Hi It didnt work

Collapse
 
tene profile image
TenE

Hey! Could you share more details on the issue? Is there a console error, or is the toggle not working as expected? Also, ensure Tailwind CSS v4.0 is set up correctly. Happy to help!

Collapse
 
jacob_nguyen_03b091a83af0 profile image
Jacob Nguyen • Edited

Could be due to the post was written in Typescript, thus useState can define the type -> useState'<'boolean'>'...

If you are writing in Javascript, just declare it as useState(...)

Collapse
 
tene profile image
TenE

Nice

Warp.dev image

The best coding agent. Backed by benchmarks.

Warp outperforms every other coding agent on the market, and gives you full control over which model you use. Get started now for free, or upgrade and unlock 2.5x AI credits on Warp's paid plans.

Download Warp

👋 Kindness is contagious

Explore this insightful piece, celebrated by the caring DEV Community. Programmers from all walks of life are invited to contribute and expand our shared wisdom.

A simple "thank you" can make someone’s day—leave your kudos in the comments below!

On DEV, spreading knowledge paves the way and fortifies our camaraderie. Found this helpful? A brief note of appreciation to the author truly matters.

Let’s Go!