React Theme Switcher Button
React theme switcher with light, dark, and system modes. Three-way toggle with system preference detection and smooth icon transitions using TypeScript and shadcn/ui.
Building theme switching interfaces?
Join our Discord community for help from other developers.
Theme switchers are everywhere—navigation bars, settings panels, mobile apps. But most are just basic toggles between light and dark. Binary. Limited. No system detection. Meanwhile, users expect intelligent theme management that respects their OS preferences and provides smooth visual transitions. This React component gives you a complete theme switcher button with light, dark, and system modes, animated icon transitions, and persistent storage that creates a seamless theming experience.
Built for React applications with TypeScript and Next.js. Uses Framer Motion for smooth icon animations and Lucide icons for crisp sun/moon rendering. The component doesn't just toggle themes—it detects system preferences, persists user choices, and provides visual feedback during transitions. Perfect for shadcn/ui design systems with consistent styling patterns and accessibility support.
Why most theme toggles feel incomplete
Developers add a light/dark toggle and think they're done with theming. Maybe they store the preference in localStorage if they're being thorough. The problem? Users have system-wide theme preferences that get ignored. There's no visual feedback during theme changes. Icon transitions feel jarring and disconnected. It's functional but not thoughtful.
This component provides three-way theme selection: light, dark, and system auto-detection. Smooth icon animations create visual continuity during theme changes. System preference detection respects user choices at the OS level. The controlled/uncontrolled pattern gives you full flexibility over state management while handling all the complex theme coordination automatically.
Built with proper ARIA attributes and keyboard navigation, so theme switching works seamlessly for all users regardless of how they interact with your interface.
Installation
npx shadcn@latest add https://www.shadcn.io/registry/theme-switcher.json
npx shadcn@latest add https://www.shadcn.io/registry/theme-switcher.json
pnpm dlx shadcn@latest add https://www.shadcn.io/registry/theme-switcher.json
bunx shadcn@latest add https://www.shadcn.io/registry/theme-switcher.json
Examples
Size Options
Three sizes for different interface densities:
Features
- Triple theme modes with light, dark, and system preference detection using JavaScript media queries
- System preference sync automatically following OS theme changes with real-time updates
- Smooth icon animations with sun/moon transitions using Framer Motion physics
- Persistent storage saving theme preferences to localStorage with automatic restoration
- Controlled/uncontrolled modes supporting both external state management and internal handling
- TypeScript definitions with complete interface definitions and theme type safety
- Accessibility features with keyboard navigation and proper ARIA labels
- Lucide icon integration for crisp, scalable sun and moon icons at any size
- Custom styling support with className props and Tailwind CSS compatibility
- Next.js optimization with SSR-safe theme detection and hydration handling
Use Cases
This free open source React component works perfectly for:
- Application headers - Global theme control in navigation bars and toolbars built with Next.js
- Settings panels - User preference configuration in app settings pages using TypeScript
- Dashboard controls - Quick theme switching in admin and data interfaces
- Content platforms - Reader-friendly dark mode toggles for articles and documentation
- Mobile applications - Native-feeling theme controls with system preference respect
- Design systems - Consistent theme switching across component libraries
- Developer tools - Code editor and IDE theme controls with professional feel
- E-commerce sites - User preference theming for shopping and browsing experiences
API Reference
ThemeSwitcher
The main component for three-way theme switching with system detection.
Prop | Type | Default | Description |
---|---|---|---|
value | 'light' | 'dark' | 'system' | - | Current theme value for controlled mode |
defaultValue | 'light' | 'dark' | 'system' | 'system' | Initial theme for uncontrolled mode |
onChange | (theme: string) => void | - | Callback when theme changes |
disabled | boolean | false | Disables theme switching interactions |
className | string | - | Additional CSS classes for button styling |
iconSize | number | 16 | Size of sun/moon icons in pixels |
animationDuration | number | 0.2 | Duration of icon transition animations |
...props | ButtonHTMLAttributes | - | All native button props supported |
Theme States
State | Icon | Behavior | Description |
---|---|---|---|
light | Sun | Fixed light theme | Forces light mode regardless of system |
dark | Moon | Fixed dark theme | Forces dark mode regardless of system |
system | Auto-switching | Follows OS preference | Automatically adapts to system theme |
System Detection
The component automatically detects and responds to:
- Initial system preference on component mount
- Real-time system changes via
prefers-color-scheme
media queries - Storage persistence preserving user selections across sessions
- SSR compatibility with Next.js hydration and server-side rendering
Animation Specifications
Element | Animation | Duration | Easing |
---|---|---|---|
Icon transition | Fade + scale | 0.2s | ease-in-out |
Button state | Background | 0.15s | ease-out |
Focus ring | Scale + opacity | 0.1s | ease-out |
Common gotchas
System theme detection timing: The component detects system preferences on mount, but initial render might show default state. This is normal for SSR compatibility and prevents hydration mismatches.
Storage persistence conflicts: If you're using multiple theme management libraries, ensure they don't conflict with localStorage keys. The component uses a specific key pattern that should be unique to your application.
Icon animation performance: Rapid theme switching can queue multiple animations. The component handles this gracefully, but very fast clicking might create visual overlap on slower devices.
CSS variable propagation: Theme changes update CSS custom properties at the document level. Ensure your theme CSS variables are properly scoped to avoid conflicts with other styling systems.
Controlled state synchronization: When using controlled mode, ensure your state updates are synchronous with the onChange callback to prevent animation glitches during theme transitions.
System preference overrides: When set to 'system' mode, manual theme changes will temporarily override system detection until the component is reset or remounted.
You might also like
Explore other interactive button components for React applications:
Theme Toggle Button
Animated dark mode switch with View Transitions API
Icon Button
Animated icon buttons with particle burst effects
Liquid Button
Animated buttons with fluid fill effects
Flip Button
3D flip animations with smooth physics transitions
Questions developers actually ask
React Text Reveal Button
React button with animated text reveal effect on hover. Smooth border animation unveils text with glowing shadows and stroke effects using TypeScript and shadcn/ui.
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.