ThemeSwitcher
Appearance mode controller providing zero-layout-shift switching between light, dark, and system color schemes, complete with a FOUC-prevention hydration script and useTheme hook.
// Radix — @radix-ui/react-dropdown-menu
import { ThemeScript, ThemeSwitcher } from "@shavin/ui";Variants
ThemeSwitcher offers 4 purpose-built variants: icon-button (cycles through modes), segmented (3-way selector), menu (Radix dropdown), and switch (binary toggle).
Icon Button
Segmented
Menu
Switch
{/* 1. Icon Button (Default) */}
<ThemeSwitcher variant="icon-button" />
{/* 2. Segmented 3-Way Control */}
<ThemeSwitcher variant="segmented" showLabels />
{/* 3. Dropdown Menu */}
<ThemeSwitcher variant="menu" showLabels />
{/* 4. Binary Switch */}
<ThemeSwitcher variant="switch" />Sizes
<ThemeSwitcher variant="segmented" size="sm" showLabels />
<ThemeSwitcher variant="segmented" size="md" showLabels />
<ThemeSwitcher variant="segmented" size="lg" showLabels />FOUC Prevention with ThemeScript
To eliminate the flash of unstyled theme during initial page load, place ThemeScript inside your root layout head:
// app/layout.tsx
import { ThemeScript } from "@shavin/ui";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" suppressHydrationWarning>
<head>
<ThemeScript />
</head>
<body>{children}</body>
</html>
);
}useTheme Hook
Directly access or control the active theme programmatically with the useTheme() hook:
import { useTheme } from "@shavin/ui";
export function CustomToggle() {
const { theme, resolvedTheme, setTheme, toggleTheme } = useTheme();
return (
<button onClick={toggleTheme}>
Current theme: {theme} (active: {resolvedTheme})
</button>
);
}Props
| Prop | Type | Default |
|---|---|---|
| variant | "icon-button" | "segmented" | "menu" | "switch" | — |
| storageKey | string | — |
| defaultTheme | ThemeMode | — |
| showLabels | boolean | — |
| labels | { | — |
| light | string | — |
| dark | string | — |
| system | string | — |
| size | "sm" | "md" | "lg" | "md" |