DEV Community

Cover image for Building a Lightweight Dark/Light Mode System in React with use-theme-mode
Saad Ahmad
Saad Ahmad

Posted on

Building a Lightweight Dark/Light Mode System in React with use-theme-mode

Dark mode has become a standard expectation in modern web applications. But implementing it correctly—with persistence, system preference detection, and clean separation of logic and styles—is often more complicated than it should be.

That’s exactly why I built use-theme-mode, a lightweight React hook that handles theme state for you—without enforcing any styling system.


✨ Why Another Theme Hook?

Most theme solutions today fall into one of two categories:

  • Too opinionated (UI framework–specific)
  • Too heavy for simple use cases

I wanted something that:

  • ✅ Works with any CSS solution
  • ✅ Persists user preference
  • ✅ Respects system theme
  • ✅ Is SSR‑safe
  • ✅ Has zero runtime dependencies

That became use-theme-mode.


📦 Installation

npm install use-theme-mode
Enter fullscreen mode Exit fullscreen mode

🚀 Basic Usage

import { useTheme } from "use-theme-mode";

function App() {
  const { theme, toggleTheme } = useTheme();

  return (
    <button onClick={toggleTheme}>
      Current theme: {theme}
    </button>
  );
}
Enter fullscreen mode Exit fullscreen mode

That’s it. Behind the scenes, the hook:

  1. Detects system preference
  2. Saves the theme to localStorage
  3. Updates the <html data-theme="..."> attribute
  4. Syncs changes across tabs

🎨 Styling Is 100% in Your Control

use-theme-mode does not inject styles. Instead, it gives you a single source of truth via data-theme.

CSS Implementation

:root[data-theme="light"] {
  --bg-color: #ffffff;
  --text-color: #000000;
}

:root[data-theme="dark"] {
  --bg-color: #121212;
  --text-color: #ffffff;
}

body {
  background-color: var(--bg-color);
  color: var(--text-color);
  transition: background 0.3s ease, color 0.3s ease;
}
Enter fullscreen mode Exit fullscreen mode

This approach works beautifully with:

  • Tailwind
  • CSS variables
  • Styled-components
  • MUI / Chakra
  • Vanilla CSS

🔄 Works Great with Tailwind Too

JavaScript Configuration:

// tailwind.config.js
module.exports = {
  darkMode: ["attribute", "data-theme"],
};
Enter fullscreen mode Exit fullscreen mode

JSX Usage:

<div className="bg-white text-black dark:bg-black dark:text-white">
  Hello world
</div>
Enter fullscreen mode Exit fullscreen mode

🌍 Live Demo

I’ve deployed a live demo showcasing:

  • Real theme switching
  • Persistent state
  • Code examples synced with UI

👉 View Live Demo


🧩 API Reference

const { theme, toggleTheme, setLight, setDark } = useTheme();
Enter fullscreen mode Exit fullscreen mode
  • theme"light" | "dark"
  • toggleTheme() → switch modes
  • setLight() → force light mode
  • setDark() → force dark mode

🛡️ SSR Safe & Production Ready

The hook:

  • Avoids direct window access during render.
  • Works in Next.js and other SSR frameworks.
  • Synchronizes changes across tabs automatically.

🔮 What’s Coming Next?

Plans for future versions:

  • ✅ TypeScript-first API
  • ✅ Optional context provider
  • ✅ ESM build support
  • ✅ Custom storage keys

🔗 Links

If you find this useful, I’d love feedback or contributions!

Top comments (0)