Theme Switcher
Theme toggle button for light and dark modes.
Installation
CLI
npx shadcn@latest add "https://ui.xaclabs.dev/r/theme-switcher.json"Usage
import { ThemeSwitcher } from "@/registry/components/theme-switcher"View component source
theme-switcher.tsx
tsx"use client"
import { Moon, Sun } from "lucide-react"
import { AnimatePresence, motion } from "motion/react"
import * as React from "react"
import { Button } from "@/components/ui/button"
export function ThemeSwitcher() {
const [isDark, setIsDark] = React.useState(true)
React.useEffect(() => {
setIsDark(document.documentElement.classList.contains("dark"))
}, [])
const toggle = React.useCallback(() => {
setIsDark((prev) => {
const next = !prev
document.documentElement.classList.toggle("dark", next)
try {
localStorage.setItem("theme", next ? "dark" : "light")
} catch {}
return next
})
}, [])
return (
<Button
variant="ghost"
size="icon"
onClick={toggle}
aria-label="Toggle theme"
>
<AnimatePresence mode="wait" initial={false}>
{isDark ? (
<motion.div
key="dark"
initial={{ opacity: 0, rotate: -90, scale: 0 }}
animate={{ opacity: 1, rotate: 0, scale: 1 }}
exit={{ opacity: 0, rotate: 90, scale: 0 }}
transition={{ duration: 0.2 }}
>
<Moon className="h-[1.2rem] w-[1.2rem]" />
</motion.div>
) : (
<motion.div
key="light"
initial={{ opacity: 0, rotate: 90, scale: 0 }}
animate={{ opacity: 1, rotate: 0, scale: 1 }}
exit={{ opacity: 0, rotate: -90, scale: 0 }}
transition={{ duration: 0.2 }}
>
<Sun className="h-[1.2rem] w-[1.2rem]" />
</motion.div>
)}
</AnimatePresence>
</Button>
)
}