Theme Toggle
atroui/theme-toggleFreeComponent
Light / system / dark toggle using next-themes.
Install it
npx shadcn@latest add https://atroui.com/r/theme-toggle.jsonSource
1"use client"23import { Monitor, Moon, Sun } from "lucide-react"4import { useTheme } from "next-themes"5import { useEffect, useState } from "react"67import { cn } from "@/lib/utils"89const OPTIONS = [10 { id: "light", label: "Light", Icon: Sun },11 { id: "system", label: "System", Icon: Monitor },12 { id: "dark", label: "Dark", Icon: Moon },13] as const1415type ThemeId = (typeof OPTIONS)[number]["id"]1617export function ThemeToggle({ className }: { className?: string }) {18 const { theme, setTheme, resolvedTheme } = useTheme()19 const [mounted, setMounted] = useState(false)2021 useEffect(() => {22 setMounted(true)23 }, [])2425 const current = (mounted26 ? (theme ?? resolvedTheme ?? "system")27 : "system") as ThemeId28 const currentOption = OPTIONS.find((o) => o.id === current) ?? OPTIONS[1]!2930 const cycle = () => {31 const idx = OPTIONS.findIndex((o) => o.id === current)32 const next = OPTIONS[(idx + 1) % OPTIONS.length]!33 setTheme(next.id)34 }3536 return (37 <>38 <button39 type="button"40 aria-label={`Theme: ${currentOption.label}. Tap to change.`}41 title={`Theme: ${currentOption.label}`}42 onClick={cycle}43 className={cn(44 "inline-flex size-9 items-center justify-center rounded-lg border border-border-subtle bg-white/5 text-foreground sm:hidden active:scale-[0.97]",45 className46 )}47 >48 <currentOption.Icon className="size-4" strokeWidth={2} />49 </button>5051 <div52 role="radiogroup"53 aria-label="Theme"54 className={cn(55 "hidden items-stretch overflow-hidden rounded-lg border border-border-subtle bg-white/5 sm:inline-flex",56 className57 )}58 >59 {OPTIONS.map(({ id, label, Icon }) => {60 const isActive = current === id61 const title =62 id === "system" && mounted && theme === "system" && resolvedTheme63 ? `System (${resolvedTheme})`64 : label65 return (66 <button67 key={id}68 type="button"69 role="radio"70 aria-checked={isActive}71 aria-label={title}72 title={title}73 onClick={() => setTheme(id)}74 className={cn(75 "inline-flex min-h-9 min-w-9 items-center justify-center text-muted-foreground",76 isActive && "bg-primary text-primary-foreground",77 !isActive && "hover:bg-white/10 hover:text-foreground"78 )}79// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from atroui
All 81 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Breadcrumbsbreadcrumbs | atroui | Components | Free | 2 deps | |
| Buttonbutton | atroui | Components | Free | 2 deps | |
| Cardcard | atroui | Components | Free | no deps | |
| Fade Infade-in | atroui | Components | Free | 1 dep | |
| Form Selectform-select | atroui | Components | Free | 2 deps | |
| Founder Avatarfounder-avatar | atroui | Components | Free | 1 dep | |
| Logologo | atroui | Components | Free | no deps | |
| Mockup Framemockup-frame | atroui | Components | Free | no deps |
