Join our Discord Community

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.

Loading component...

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:

Loading component...

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.

PropTypeDefaultDescription
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
disabledbooleanfalseDisables theme switching interactions
classNamestring-Additional CSS classes for button styling
iconSizenumber16Size of sun/moon icons in pixels
animationDurationnumber0.2Duration of icon transition animations
...propsButtonHTMLAttributes-All native button props supported

Theme States

StateIconBehaviorDescription
lightSunFixed light themeForces light mode regardless of system
darkMoonFixed dark themeForces dark mode regardless of system
systemAuto-switchingFollows OS preferenceAutomatically 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

ElementAnimationDurationEasing
Icon transitionFade + scale0.2sease-in-out
Button stateBackground0.15sease-out
Focus ringScale + opacity0.1sease-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:

Questions developers actually ask