Theme Toggle
An icon button that flips between light and dark with a circular reveal.
Overview
ThemeToggle is an IconButton wired to
next-themes. Clicking it flips the
theme between light and dark; on browsers that support the
View Transitions API
the change plays as an expanding circle wiped out from the button, and everywhere
else it falls back to an instant swap. The Sun/Moon glyph always crossfades with a
subtle scale and blur.
It inherits IconButton's geometry, state layers and focus ring, so variant,
size and shape pass straight through — it defaults to the low-emphasis
standard style.
$ npx morpha-ui add theme-toggleExamples
Variants
Because it forwards to IconButton, every button style is available. Use
standard for a bare toolbar icon, or a contained style when it needs more
presence.
Sizes
The square IconButton sizes apply here too.
Usage
ThemeToggle reads the theme from a next-themes provider, so it must render
inside one. In this Fumadocs site that provider is already supplied by
RootProvider; in a plain Next.js app, wrap your tree once at the root:
import { ThemeProvider } from "next-themes";
import { ThemeToggle } from "@/components/ui/theme-toggle";
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
<header>
<ThemeToggle />
</header>
{children}
</ThemeProvider>
);
}Add suppressHydrationWarning to your <html> element, as next-themes
requires.
Installation
Morpha UI components are distributed via the Morpha CLI (coming in a later
step): npx morpha-ui add theme-toggle. It depends on icon-button,
next-themes and motion.
API Reference
Prop
Type
Accessibility
- Renders a native
<button>viaIconButton, so keyboard activation and focus handling come for free. - Ships a default
aria-labelof "Toggle theme"; override it to match your wording or locale. - A static placeholder with the same footprint renders until mount, avoiding a hydration mismatch and layout shift.
- The circular reveal is skipped where it can't play reliably (no View Transitions support, or macOS); the theme still changes.
- The icon crossfade keeps its opacity change under
prefers-reduced-motion.