How to Implement Dark Mode in Astro with Tailwind CSS
Want to add dark mode to your Astro site using Tailwind CSS? This step-by-step guide shows you how to seamlessly integrate a user-friendly dark/light theme toggle with minimal code. We’ll use Astro’s inline scripts and Preact for a smooth, flash-free implementation.
Why Dark Mode Matters
Dark mode reduces eye strain, saves battery life, and improves accessibility. With Tailwind CSS, enabling it is straightforward—just toggle a dark
class on your HTML element.
“Dark mode isn’t just a trend—it’s a usability enhancement.”
Setting Up Your Astro Project
Install Dependencies
First, create a new Astro project and add Tailwind CSS + Preact:
npm create astro@latest
npm install -D @astrojs/tailwind @astrojs/preact
npm install preact
Configure Tailwind for Dark Mode
Update tailwind.config.cjs
to enable class-based dark mode:
module.exports = {
content: ["./src/**/*.{js,ts,jsx,tsx,astro}"],
darkMode: "class",
theme: {},
plugins: [],
};
Implementing Theme Detection
Inline Script for Instant Loading
Add this script to your Layout.astro
to prevent theme flickering:
<script is:inline>
const theme = (() => {
if (typeof localStorage !== "undefined" && localStorage.getItem("theme")) {
return localStorage.getItem("theme");
}
if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
return "dark";
}
return "light";
})();
document.documentElement.classList.toggle("dark", theme === "dark");
localStorage.setItem("theme", theme);
</script>
How it works:
- Checks for saved user preference in
localStorage
. - Falls back to system preferences.
- Applies the
dark
class instantly.
Building the Theme Toggle
Preact Component Example
Create a toggle button with state management:
import { useEffect, useState } from "preact/hooks";
export default function ThemeToggle() {
const [theme, setTheme] = useState("light");
useEffect(() => {
const savedTheme = localStorage.getItem("theme") ||
(window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light");
setTheme(savedTheme);
}, []);
const toggleTheme = () => {
const newTheme = theme === "light" ? "dark" : "light";
setTheme(newTheme);
document.documentElement.classList.toggle("dark", newTheme === "dark");
localStorage.setItem("theme", newTheme);
};
return <button onClick={toggleTheme}>{theme === "light" ? "🌙" : "🌞"}</button>;
}
Handling Server-Side Rendering
Avoiding Hydration Mismatches
For SSG, use a mounted
state to delay rendering until client-side APIs are available:
const [isMounted, setIsMounted] = useState(false);
useEffect(() => {
setIsMounted(true);
}, []);
if (!isMounted) return null; // Skip SSR render
Key takeaways:
- Prevents
localStorage
errors during build. - Ensures consistent theming after hydration.
Final Tips for Optimization
- Use Tailwind’s
dark:
prefix for dark-mode styles (e.g.,dark:bg-gray-900
). - Test across devices to ensure theme persistence.
- Consider adding a transition for smoother toggling.
#TailwindCSS #Astro #DarkMode #WebDev