A theme switch that wipes the new theme across the page with the View Transitions API.
Installation
Usage
Bring your own theme state: ThemeToggler takes the theme and its setter as props, so it works with any theme library. With next-themes:
import { useTheme } from 'next-themes';
import {
ThemeToggler,
type Resolved,
type ThemeSelection,
} from '@animate-ui/react/primitives/effects/theme-toggler';
export function ThemeButton() {
const { theme, resolvedTheme, setTheme } = useTheme();
return (
<ThemeToggler
theme={theme as ThemeSelection}
resolvedTheme={resolvedTheme as Resolved}
setTheme={setTheme}
>
{({ effective, toggleTheme }) => (
<button
onClick={() => toggleTheme(effective === 'dark' ? 'light' : 'dark')}
>
{effective === 'dark' ? 'Light mode' : 'Dark mode'}
</button>
)}
</ThemeToggler>
);
}During the wipe, the dark class is toggled on <html>: this matches class-based dark mode (Tailwind's default, next-themes with attribute="class").
Animation
- The new theme is revealed with a clip-path wipe over 700ms with an
ease-in-out, indirection(left to right by default). The old and new pages are snapshots, so nothing re-renders mid-wipe. setThemeis called once the wipe finishes;onImmediateChangefires right away.- Switching to
systemwhen it resolves to the current theme changes nothing on screen, so there is no wipe. - Browsers without the View Transitions API swap the theme at once.
<AnimateUIProvider tempo>paces the wipe.- Under reduced motion, the theme swaps at once, without the wipe.
- On the server and while hydrating, the render function receives
effective: 'system'andresolved: 'light', since the theme is only known on the client.
API Reference
ThemeToggler
| Prop | Type | Default |
|---|---|---|
theme | 'light' | 'dark' | 'system' | - |
resolvedTheme | 'light' | 'dark' | - |
setTheme | (theme: 'light' | 'dark' | 'system') => void | - |
direction? | 'btt' | 'ttb' | 'ltr' | 'rtl' | 'ltr' |
onImmediateChange? | (theme: 'light' | 'dark' | 'system') => void | - |
children? | React.ReactNode | ((state: { resolved: 'light' | 'dark'; effective: 'light' | 'dark' | 'system'; toggleTheme: (theme: 'light' | 'dark' | 'system') => void }) => React.ReactNode) | - |
Credits
- Inspired by Magic UI's animated theme toggler.