Theme Switcher
next-themes.<head> the first time a switcher mounts — so an installed copy works without touching globals.css. Browsers without View Transitions, and users who prefer reduced motion, get an instant switch.next-themes, so flipping one changes the theme for the whole docs site. The previews run in their own frame, and a View Transition can't cross documents, so here the demo hands the pick to the page through applyTheme — what you see is the header's own reveal, starting from the control you clicked. In your app the component does all of that itself.Icon
One button that flips between light and dark, with a spin micro-interaction on the icon and the circular reveal growing out of it.
variant"icon"- is the default — the compact choice for a navbar.
Installation
New project? Follow the installation guide to set up Tailwind CSS, shadcn, the cn helper and lucide-react, register the @algorize-ui registry once, then copy your styles from the Themes page.
Install Theme Switcher
components/ is written into your project. Copy any snippet from a Usage tab above and it runs.
Install dependencies
Step 1 installs this for you. Run it yourself only if you are adding Theme Switcher to an existing project by hand. The cn helper and lucide-react are not listed — the installation guide covers them once for every component.
next-themes is an npm package.
Configure next-themes
ThemeSwitcher reads and writes the theme through next-themes, so your app has to be wrapped in its provider once. suppressHydrationWarning on <html> is required — the theme class is applied before React hydrates.Examples
Features
Circular Reveal
The new theme sweeps out from whichever control was clicked, via the View Transitions API. The ::view-transition rules it needs ship inside the component, so nothing has to be added to your global stylesheet. Set reveal= for an instant switch.
Self-contained
Every style the switcher relies on is in ThemeSwitcherBlock.tsx: Tailwind classes for the controls, and the reveal CSS injected once into <head>. Install it and it works; nothing to copy into globals.css.
Hydration Safe
Safely checks if the component is mounted on the client to completely eliminate the infamous React hydration mismatch errors.
No Animation Library
The sliding pill, the icon spin and the dropdown run on the browser's built-in Web Animations API, so they feel completely native and add no dependency.
3 Layout Variants
Ships with a compact Icon toggle, a sliding Pill control, and a classic Dropdown menu to fit any UI design.
System Sync
Automatically syncs with the user's OS-level dark mode preferences if the "system" theme is selected.
Best Practices & Guidelines
SSR Considerations
Since next-themes relies on localStorage which isn't available during Server-Side Rendering, always wrap the component's inner rendering logic in a mounted check to prevent hydration mismatch flashes.
Props Reference
| Component | Prop | Type | Default | Description |
|---|---|---|---|---|
ThemeSwitcher | variant | "icon" | "pill" | "dropdown" | "icon" | Determines the structural layout and specific animations applied
to the toggler.
|
ThemeSwitcher | reveal | boolean | true | Grow the new theme out of the clicked control as a circle. Skipped
automatically when the browser lacks the View Transitions API, the
user prefers reduced motion, or nothing visible would change (for
example picking system while it already resolves to the
current theme). |
ThemeSwitcher | revealDuration | number | 500 | Length of the reveal, in milliseconds. |
ThemeSwitcher | applyTheme | (theme: string, origin: Element | null) => void | undefined | Replace how a picked theme is applied. By default the switcher reveals
and calls setTheme; pass this to do it yourself — persist
it first, or hand the switch to a parent document. origin
is the element that was clicked, for positioning your own reveal. |
ThemeSwitcher | ...props | HTMLAttributes<HTMLDivElement> | undefined | Accepts all native HTML div attributes (e.g., className,
id) which are seamlessly forwarded to the root wrapper. |