React Theme Toggle Button
React dark mode toggle with animated sun/moon icons and View Transitions API. Smooth theme switching with multiple animation effects, TypeScript, and shadcn/ui.
Powered by
Building theme switching interfaces?
Join our Discord community for help from other developers.
Dark mode toggles are everywhere—navigation bars, settings panels, user dashboards. But most just swap colors instantly. Jarring. Disconnected. No visual continuity. Meanwhile, users expect smooth transitions that feel intentional and polished. This React component gives you cinematic theme switching powered by the View Transitions API, creating seamless visual bridges between light and dark modes that delight users.
Built for React applications with TypeScript and Next.js. Uses the modern View Transitions API for hardware-accelerated animations that feel native. The transitions aren't random—they use precise masking and clip-path animations that create visual continuity. Works with any theme management solution including next-themes, custom contexts, or global state.
Why most theme toggles feel abrupt
Developers just toggle a CSS class and call it done. Maybe they add a basic fade if they're being fancy. The problem? Users lose visual context during the transition. Their brain needs to reprocess the entire interface. It feels like switching between different apps, not different versions of the same app.
The View Transitions API solves this by creating smooth morphing animations between DOM states. The expanding circle creates anticipation and guides the eye. The blur effects add depth and sophistication. Custom GIF masks let you match your brand personality. It's not just a toggle—it's a mini-experience.
This component handles the complex API integration, fallback management, and animation cleanup automatically. Multiple variants ensure you can match any design aesthetic while maintaining smooth performance across devices.
Installation
npx shadcn@latest add https://www.shadcn.io/registry/theme-toggle-button.json
npx shadcn@latest add https://www.shadcn.io/registry/theme-toggle-button.json
pnpm dlx shadcn@latest add https://www.shadcn.io/registry/theme-toggle-button.json
bunx shadcn@latest add https://www.shadcn.io/registry/theme-toggle-button.json
Examples
GIF Mask Animations
Animation Positions
Circle animations can start from different positions for directional effects:
Features
- View Transitions API integration for hardware-accelerated theme switching animations
- Multiple animation variants including circle, blur, polygon and custom GIF masks
- Flexible positioning with animations starting from center, corners, or edges
- Framework agnostic works with any theme management solution (next-themes, custom context)
- TypeScript definitions with complete interface definitions and IntelliSense
- Accessible controls with proper ARIA labels and keyboard navigation support
- Automatic fallbacks for browsers without View Transitions API support
- Performance optimized with dynamic CSS injection and automatic cleanup
Use Cases
This free open source React component works perfectly for:
- Navigation headers - Elegant theme switching for Next.js applications
- Settings interfaces - Beautiful preference controls using TypeScript
- Admin dashboards - Professional theme buttons with shadcn/ui integration
- Landing pages - Memorable user experiences with custom animations
- Mobile apps - Native-feeling theme toggles that respond smoothly
- Creative portfolios - Artistic transitions that showcase attention to detail
API Reference
ThemeToggleButton
The main component for animated theme switching with View Transitions.
Prop | Type | Default | Description |
---|---|---|---|
theme | 'light' | 'dark' | 'light' | Current theme state |
onClick | () => void | - | Click handler for theme toggle |
variant | 'circle' | 'circle-blur' | 'gif' | 'polygon' | 'circle' | Animation variant to use |
start | 'center' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'center' | Starting position for circle animations |
showLabel | boolean | false | Show text label next to icon |
url | string | - | URL of GIF to use as mask (required for gif variant) |
className | string | - | Additional CSS classes |
disabled | boolean | false | Disables button and prevents animations |
...props | ButtonHTMLAttributes | - | All native button props supported |
Animation Variant Specifications
Variant | Effect | Performance | Use Case |
---|---|---|---|
circle | Expanding circle mask | Excellent | Clean, minimal interfaces |
circle-blur | Circle with gaussian blur | Very good | Soft, premium feel |
polygon | Diagonal clip-path wipe | Excellent | Modern, geometric designs |
gif | Custom GIF as mask | Good | Brand-specific animations |
Position Specifications
Position | Origin Point | Visual Effect |
---|---|---|
center | Button center | Balanced expansion |
top-left | Upper left corner | Diagonal sweep right-down |
top-right | Upper right corner | Diagonal sweep left-down |
bottom-left | Lower left corner | Diagonal sweep right-up |
bottom-right | Lower right corner | Diagonal sweep left-up |
useThemeTransition Hook
Helper hook that wraps the View Transitions API with automatic fallbacks:
const { startTransition } = useThemeTransition();
// Use it to wrap your theme update
startTransition(() => {
setTheme(newTheme);
// Any other DOM updates
});
Browser Support
View Transitions API support status:
- Chrome/Edge 111+ - Full animation support with hardware acceleration
- Opera 97+ - Complete feature set with smooth performance
- Safari 18+ - Experimental support, enable in Feature Flags
- Firefox - Not yet supported, gracefully falls back to instant toggle
Note: In unsupported browsers, the component automatically falls back to immediate theme switching without animations.
Common gotchas
Animation flickering on fast clicks: Rapid theme toggling can cause overlapping transitions. The component debounces clicks automatically, but very fast clicking might create visual conflicts in some browsers.
Custom theme properties not transitioning: The View Transitions API only animates CSS properties that can be interpolated. Custom properties using discrete values (like font families) won't animate smoothly.
GIF performance impact: Custom GIF masks can be resource-intensive, especially large or high-framerate GIFs. Consider optimizing GIFs for web use or using simpler animations for better performance.
Z-index layering during transition: The transition creates a temporary overlay that might interfere with high z-index elements like modals or dropdowns. Ensure proper stacking context for complex layouts.
Theme context timing: Make sure theme updates happen inside the startTransition callback for proper synchronization. Updating theme state before or after the transition can cause visual inconsistencies.
Safari flag requirement: Safari users need to manually enable View Transitions in Settings > Advanced > Feature Flags. Consider showing instructions or using feature detection to inform users.
You might also like
Explore other interactive button components for React applications:
Icon Button
Animated icon buttons with particle burst effects
Liquid Button
Animated buttons with fluid fill effects
Ripple Button
Material Design ripple effects from click position
Flip Button
3D flip animations with smooth physics transitions