shadcn.io is not affiliated with official shadcn/ui
Each press moves to the next theme, and the icon turns to match: a sun for light, a moon for dark and, with system, a monitor for the device's own setting. The button only reports the new theme through onThemeChange; your app applies it to the page.
Use it in a header or a settings bar, wherever people expect a one-press switch. When people need to see all the choices at once, a menu or a segmented control says more. It wraps the Button, so variant and size work as usual. It doesn't depend on any theme library: control it with theme and onThemeChange, or leave it uncontrolled. The theme is one data attribute on the icon slot, and CSS transitions turn, grow and fade the icons, with no animation library; with reduced motion they only cross-fade.
Installation
Usage
import { ThemeToggleButton } from "@/components/ui/theme-toggle-button";<ThemeToggleButton onThemeChange={setTheme} theme={theme} />Demos
Controlled
Pass theme and update it from onThemeChange to keep the button in step with the rest of your app. Here a row of options and the toggle share one state, so changing either updates both. In your app, the same callback applies the theme to the page.
System Theme
system adds a third step to the cycle: light, dark, then system, shown as a monitor, which means follow the device. Use it when your theme setup stores system as its own choice. The last one here is round and larger, with pill and size="icon-lg".
Accessibility
The button is named by what it does next, such as Switch to dark theme, and the name changes after each press. Pass aria-label to use your own wording. The icons are hidden from assistive tech.
- Enter
- Switch to the next theme.
- Space
- Switch to the next theme.
FAQ
Why doesn't it change my page's theme?
It only reports the next theme, so it works with any setup: a theme provider, a class on the root element, or a stored setting. Apply the theme in onThemeChange, and pass the current one back as theme.
How do I use it with a theme provider?
Pass the provider's current theme as theme and its setter as onThemeChange. If the provider uses a system theme, add system so the button can show and choose it.
Do I have to pass theme?
No. Without it the button keeps its own state, starting from defaultTheme, which is light. onThemeChange still reports each change.
How do I avoid the wrong icon on first load?
The server doesn't know the visitor's theme, so render the button with the theme you'll show first, or wait until your provider has read the stored theme before passing theme.
API Reference
ThemeToggleButton
data-slot="theme-toggle-button-icon"
theme?ThemeToggleTheme
The theme it shows, when you hold it yourself. Pair it with
onThemeChange.
defaultTheme?ThemeToggleTheme
The theme it starts on when you don't pass theme.
- Default
"light"
onThemeChange?(theme: ThemeToggleTheme) → void
Called with the next theme on every press. This is where you apply it to your page: the button never changes the page's theme itself.
- Parameters
theme– ThemeToggleTheme- Returns
void
focusableWhenDisabled?boolean
Whether the button should be focusable when disabled.
- Default
false
nativeButton?boolean
Whether the component renders a native <button> element when replacing it
via the render prop.
Set to false if the rendered element is not a button (for example, <div>).
- Default
true
size?ButtonProps["size"]
The Button size; an icon size, since it shows only an icon.
- Default
"icon-md"
system?boolean
Add the system theme to the cycle: light, dark, system, then light again. Without it, the button switches between light and dark.
- Default
false
render?ReactElement | ((props: object, state: object) => ReactElement)
Replace the host element or compose it with another component.
- Type
ReactElement | ((props: object, state: object) => ReactElement)
className?string
Merged with the part's classes.
data-state?string
Mirrors shown.
Related
Icon Button
Hover revealsIcon toggles for other settings
- Pro
Flip Button
Hover revealsAnother two-state swap animation
Kbd Button
ControlsGive the toggle a keyboard shortcut
- Pro
Glass Button
SurfacesA header button that suits both themes
Copy Button
Progress & resultsIcon swap that confirms an action
- Pro
Liquid Button
Hover revealsA fill effect for the active state