Dark Mode in 1 Line of Code — Meet vartheme
If you've ever implemented dark mode in React, you already know the struggle. You end up: Setting up context Writing localStorage logic Handling system preferences Fixing flash of white on load (FOUC) Wiring CSS variables And before you know it… you've written 200+ lines of boilerplate that has noth
SUMIT
If you've ever implemented dark mode in React, you already know the struggle.
You end up:
- Setting up context
- Writing localStorage logic
- Handling system preferences
- Fixing flash of white on load (FOUC)
- Wiring CSS variables
And before you know it… you've written 200+ lines of boilerplate that has nothing to do with your actual app.
So I built vartheme.
🚨 The Problem With Dark Mode Today
Most developers fall into one of these approaches:
1. Roll Your Own
- ❌ 200+ lines of code
- ❌ Breaks in SSR / Next.js
- ❌ Repeated effort every project
2. Use next-themes
- ✅ Good library
- ❌ Tied to Next.js
- ❌ No CSS variables
- ❌ No built-in UI
3. Use UI Library Themes
- ❌ Locked into their design system
- ❌ Limited flexibility
💡 What I Wanted
A solution that:
- ✅ Works in any React setup (Vite, Next.js, Remix, CRA)
- ✅ Uses CSS variables
- ✅ Includes a beautiful toggle UI
- ✅ Is SSR safe
- ✅ Persists user preferences
⚡ Introducing vartheme
A simple, flexible, and powerful theming solution for React.
🚀 Dark Mode in 1 Line
npm install vartheme
import { ThemeProvider, ThemeToggle } from 'vartheme'
export default function App() {
return (
<ThemeProvider>
<ThemeToggle />
</ThemeProvider>
)
}
That’s it.
You instantly get:
- ✅ Light / Dark / System mode
- ✅ Animated toggle (🌞 / 🌙)
- ✅ localStorage + cookie persistence
- ✅ CSS variables auto-injected
- ✅ SSR safe
🎨 Using CSS Variables
.card {
background: var(--vt-surface);
color: var(--vt-text);
border: 1px solid var(--vt-border);
}
.button {
background: var(--vt-primary);
color: white;
}
🎭 Built-in Themes
import { useThemeContext } from 'vartheme'
const { setTheme } = useThemeContext()
setTheme('ocean')
setTheme('forest')
⚙️ Next.js (App Router) — Fully SSR Safe
const isBrowser = typeof window !== 'undefined'
export function saveMode(mode) {
if (!isBrowser) return
localStorage.setItem('vartheme-mode', mode)
}
import { getFOUCScript } from 'vartheme'
export default function RootLayout({ children }) {
return (
<html>
<head>{getFOUCScript()}</head>
<body>{children}</body>
</html>
)
}
import { cookies } from 'next/headers'
import { loadModeFromCookieString } from 'vartheme'
export default function RootLayout({ children }) {
const mode =
loadModeFromCookieString(cookies().toString()) || 'system'
return <html data-theme={mode}>{children}</html>
}
🧩 shadcn/ui & Radix UI Support
<ThemeProvider strategy="class" />
🧠 useThemeContext Hook
import { useThemeContext } from 'vartheme'
function Navbar() {
const { resolvedMode, toggle } = useThemeContext()
return (
<button onClick={toggle}>
{resolvedMode === 'dark' ? '☀️ Light' : '🌙 Dark'}
</button>
)
}
🎨 Custom Colors
<ThemeProvider colors={{ primary: '#EC4899' }} />
🌬 Tailwind Plugin
import { varthemePlugin } from 'vartheme/tailwind'
export default {
plugins: [varthemePlugin],
}
📦 Package Details
- 📦 16.8kb
- ⚡ Zero dependencies
- 🧠 TypeScript support
🚀 Get Started
npm install vartheme
❤️ Final Note
Built this because I was tired of rewriting the same dark mode logic in every project.
If this helps you:
👉 Drop a ⭐ on GitHub
👉 Share it with other devs 🚀
Found this useful? Share it!
Read the Full Story
Continue reading on Dev.to
Related Stories
Majority Element
about 2 hours ago
Building a SQL Tokenizer and Formatter From Scratch — Supporting 6 Dialects
about 2 hours ago
Markdown Knowledge Graph for Humans and Agents
about 2 hours ago

Moving Beyond Disk: How Redis Supercharges Your App Performance
about 2 hours ago