Dark Mode in React
CSS-only dark mode — no JS provider, no flash, no FOUC. Toggle the .dark class and the entire app re-themes.
// Toggle dark mode — just toggle a class
function ThemeToggle() {
const [dark, setDark] = useState(false);
return (
<button onClick={() => {
document.documentElement.classList.toggle("dark");
setDark(!dark);
}}>
{dark ? "🌙" : "☀️"}
</button>
);
}Key features
No JavaScript theme provider — dark mode is pure CSS
No flash of wrong theme — CSS applies before React hydrates
No FOUC (Flash of Unstyled Content)
Toggle via .dark class on <html> — works with any toggle mechanism
All 5 theme presets include dark mode variants
Respects prefers-color-scheme with a tiny inline script
The flash problem
JavaScript-based dark mode has a flash problem: the browser renders the page with the default (light) theme, then React hydrates and switches to dark. Users see a flash of light theme before the dark theme applies.
Ninna UI solves this with a tiny inline script in <head> that checks localStorage and adds the .dark class before any content renders. The script is 200 bytes and runs synchronously — no flash.
The theme-init script
Add this script to your <head> to prevent dark mode flash:
// public/theme-init.js — included in <head> before React loads
(function() {
var theme = localStorage.getItem("theme");
if (theme === "dark" || (!theme && matchMedia("(prefers-color-scheme: dark)").matches)) {
document.documentElement.classList.add("dark");
}
})();