Author: Samantha Laine King
Last Updated: October 2025
🪄 Overview
This guide walks through adding a clean, accessible Dark Mode toggle to a React application using Tailwind CSS and localStorage.
It covers setup, theme persistence, and best practices for accessibility.
⚙️ Step 1: Set Up Tailwind for Theming
Tailwind provides built-in dark mode support.
In your tailwind.config.js
file, enable class
mode so users can manually toggle it:
// tailwind.config.js
export default {
darkMode: 'class',
theme: {
extend: {},
},
plugins: [],
}
This allows you to switch themes by adding or removing the dark class on the root element.
🧩 Step 2: Create a Custom Hook for Theme Management
We’ll use a custom React hook to manage theme state and persist it in localStorage.
// useDarkMode.ts
import { useEffect, useState } from 'react'
export default function useDarkMode() {
const [theme, setTheme] = useState(localStorage.theme || 'light')
useEffect(() => {
const root = window.document.documentElement
const isDark = theme === 'dark'
root.classList.toggle('dark', isDark)
localStorage.setItem('theme', theme)
}, [theme])
return { theme, setTheme }
}
💡 Step 3: Add the Toggle Button
Here’s how to create a reusable toggle button that switches between light and dark modes.
// ThemeToggle.tsx
import { Moon, Sun } from 'lucide-react'
import useDarkMode from './useDarkMode'
export default function ThemeToggle() {
const { theme, setTheme } = useDarkMode()
return (
<button
onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
className="p-2 rounded-full border text-gray-700 dark:text-gray-100 hover:bg-gray-100 dark:hover:bg-gray-700 transition"
aria-label="Toggle dark mode"
>
{theme === 'dark' ? <Sun size={18} /> : <Moon size={18} />}
</button>
)
}
🎨 Step 4: Test Your Dark Mode
Wrap your app in a container that uses Tailwind’s dark variant:
// App.tsx
import ThemeToggle from './ThemeToggle'
export default function App() {
return (
<div className="min-h-screen bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100 transition-colors">
<header className="flex justify-between p-4">
<h1 className="text-xl font-semibold">Dark Mode Example</h1>
<ThemeToggle />
</header>
<main className="p-4">
<p>Try toggling dark mode and refresh the page to see it persist!</p>
</main>
</div>
)
}
✅ Step 5: Best Practices & Accessibility Tips
Step 5: Best Practices & Accessibility Tips
Provide sufficient color contrast (use tools like contrast-ratio.com).
Add an aria-label to the toggle button for screen readers.
Ensure motion preferences are respected when using transitions.
Store the user’s preference in localStorage for a consistent experience.
✨ Summary
This implementation gives users control over their theme while maintaining accessibility and performance.
The setup is lightweight, scalable, and easy to extend for future design systems.
Thanks for reading!
Written with care by Samantha Laine King — Frontend Developer passionate about clean UI, accessibility, and developer education.
Top comments (0)