Join our Discord Community

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.

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.

Loading component...

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

Loading component...

Animation Positions

Circle animations can start from different positions for directional effects:

Loading component...

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.

PropTypeDefaultDescription
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
showLabelbooleanfalseShow text label next to icon
urlstring-URL of GIF to use as mask (required for gif variant)
classNamestring-Additional CSS classes
disabledbooleanfalseDisables button and prevents animations
...propsButtonHTMLAttributes-All native button props supported

Animation Variant Specifications

VariantEffectPerformanceUse Case
circleExpanding circle maskExcellentClean, minimal interfaces
circle-blurCircle with gaussian blurVery goodSoft, premium feel
polygonDiagonal clip-path wipeExcellentModern, geometric designs
gifCustom GIF as maskGoodBrand-specific animations

Position Specifications

PositionOrigin PointVisual Effect
centerButton centerBalanced expansion
top-leftUpper left cornerDiagonal sweep right-down
top-rightUpper right cornerDiagonal sweep left-down
bottom-leftLower left cornerDiagonal sweep right-up
bottom-rightLower right cornerDiagonal 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:

Questions developers actually ask