DEV Community

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

Posted on • Edited on

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

Top comments (7)

Collapse
 
elashry profile image
Ahmed Elashry

any one followed the tutorial and dark mode didn't correctly try to add dark utility classes at the main app component like below

function App() {
  return (
    <div className="dark:bg-gray-800 dark:text-white">

    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
tene profile image
TenE

Great 👍

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

Some comments may only be visible to logged-in visitors. Sign in to view all comments.