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>
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 *));
-
@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.
- Creates a custom Tailwind variant named
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 };
}
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 thehtml
class, and stores the new theme inlocalStorage
. -
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>
);
}
Explanation
-
Imports
useDarkMode
Hook: ExtractstoggleDarkMode
andisDarkMode
from the hook. -
UI Structure: A switch-like button that changes its position based on
isDarkMode
. -
Click Handler: Calls
toggleDarkMode
to switch themes.
Now you can import and use <ToggleDarkMode />
anywhere in your application!
Demo
Check out the live demo: Dark Mode Toggle
Top comments (0)