Mode Toggle

PreviousNext

An animated button component for switching between light and dark themes.

Installation

pnpm dlx bna-ui add mode-toggle

Usage

import { ModeToggle } from '@/components/ui/mode-toggle';
<ModeToggle />

Examples

Default

API Reference

ModeToggle

An animated button that toggles between light and dark themes. The component uses the Button component internally, so it inherits button styling and behavior.

PropTypeDefaultDescription
hapticbooleantrueWhether to trigger haptic feedback on press. Forwarded to the underlying Button.
variantButtonVariant'outline'The visual variant passed through to the underlying Button.
sizeButtonSize'icon'The size passed through to the underlying Button.

Animation Details

Icon Transition

  • Scale Animation: Icons scale down to 0, change, then scale back to 1
  • Duration: 150ms for each scale phase (300ms total)
  • Icons: Sun for light mode, Moon for dark mode

Sun Rotation

  • Rotation: 180° rotation when switching to light mode
  • Duration: 300ms
  • Effect: Creates a spinning sun rays effect
  • Timing: Only rotates when switching to sun icon

Theme Integration

The component is a thin shell over the useModeToggle hook — it takes isDark to pick the icon and toggleMode for the press handler, and owns nothing else:

const { toggleMode, isDark } = useModeToggle();

That hook reads the mode from ModeProvider, so a provider has to be mounted above the toggle or it throws. Wrapping your app in ThemeProvider is enough — it mounts one — and every scaffold from npx bna-ui init already does.

Performance

The component is optimized for smooth animations:

  • Uses react-native-reanimated for 60fps animations
  • Animations run on the UI thread
  • Minimal re-renders with useSharedValue
  • Efficient icon switching with runOnJS

Accessibility

The ModeToggle maintains accessibility:

  • Uses semantic button component
  • Screen readers announce theme changes
  • Maintains proper focus behavior
  • Works with keyboard navigation
  • Respects system accessibility settings

Integration Example

// app/_layout.tsx
import { ModeToggle } from '@/components/ui/mode-toggle';
import { ThemeProvider } from '@/providers/theme-provider';
 
export default function RootLayout() {
  return (
    <ThemeProvider>
      <View style={styles.header}>
        <Text>My App</Text>
        <ModeToggle />
      </View>
      {/* Rest of your app */}
    </ThemeProvider>
  );
}

Add storage={SecureStore} to ThemeProvider to have the choice survive a restart — see ModeProvider.

Dependencies

The component requires these utilities to function:

  • useModeToggle: Hook for theme switching logic
  • Button: Base button component
  • Icon: Themed icon wrapper
  • ModeProvider: Holds the mode, mounted for you by ThemeProvider