# Switch

> A control that allows the user to toggle between checked and not checked states.

**BNA UI** — a React Native / Expo component library.
These components render through `react-native`, not the DOM: there are no HTML
elements, no Tailwind classes and no Radix primitives. Source is copied into your
project and imported through `@/components/ui/*`, `@/components/charts/*`,
`@/hooks/*` and `@/theme/*`. Colours come from the `useColor` hook rather than
hardcoded hex; sizing tokens (`HEIGHT`, `FONT_SIZE`, `BORDER_RADIUS`, `CORNERS`)
come from `@/theme/globals`.

- Docs: https://ui.ahmedbna.com/docs/components/switch
- Markdown: https://ui.ahmedbna.com/docs/components/switch.md
- Structured JSON (props, usage, source, examples): https://ui.ahmedbna.com/r/ai/switch.json
- Install payload (source plus every file it imports): https://ui.ahmedbna.com/r/switch.json
- Install: `npx bna-ui add switch`
- npm dependencies: `expo-haptics`
- Registry dependencies: `mode-provider`, `useColorScheme`, `colors`, `useColor`, `useHaptics`, `globals`, `text`, `view`
- Preview recording: https://demo.ahmedbna.com/0287-switch-demo.MP4

---

**Example:** A basic switch with label

```tsx
// components/demo/switch/switch-demo.tsx
import { Switch } from '@/components/ui/switch';
import React, { useState } from 'react';

export function SwitchDemo() {
  const [isEnabled, setIsEnabled] = useState(false);

  return (
    <Switch
      label='Enable notifications'
      value={isEnabled}
      onValueChange={setIsEnabled}
    />
  );
}
```

## Installation

### CLI

```bash
npx bna-ui add switch
```

### Manual

**1.** Copy and paste the following code into your project.

```tsx
// components/ui/switch.tsx
import { Text } from '@/components/ui/text';
import { View } from '@/components/ui/view';
import { useColor } from '@/hooks/useColor';
import { useHaptics } from '@/hooks/useHaptics';
import React from 'react';
import {
  Switch as RNSwitch,
  SwitchProps as RNSwitchProps,
  TextStyle,
} from 'react-native';

interface SwitchProps extends RNSwitchProps {
  label?: string;
  error?: string;
  labelStyle?: TextStyle;
  haptic?: boolean;
}

export function Switch({
  label,
  error,
  labelStyle,
  haptic = true,
  onValueChange,
  ...props
}: SwitchProps) {
  const mutedColor = useColor('muted');
  const primary = useColor('primary');
  const danger = useColor('red');
  const feedback = useHaptics(haptic);

  const handleValueChange = React.useCallback(
    (value: boolean) => {
      feedback(value ? 'toggle-on' : 'toggle-off');
      onValueChange?.(value);
    },
    [feedback, onValueChange]
  );

  return (
    <View style={{ marginBottom: 8 }}>
      <View
        style={{
          flexDirection: 'row',
          alignItems: 'center',
          justifyContent: 'space-between',
          minHeight: 32, // Ensure consistent height
        }}
      >
        {label && (
          <Text
            variant='caption'
            numberOfLines={2} // Allow wrapping for longer labels
            ellipsizeMode='tail'
            style={[
              {
                color: error ? danger : primary,
                flex: 1, // Take available space
                marginRight: 12, // Add spacing between label and switch
              },
              labelStyle,
            ]}
            pointerEvents='none'
          >
            {label}
          </Text>
        )}

        <RNSwitch
          trackColor={{ false: mutedColor, true: '#7DD87D' }}
          thumbColor={props.value ? '#ffffff' : '#f4f3f4'}
          accessibilityLabel={label}
          {...props}
          onValueChange={handleValueChange}
        />
      </View>

      {error && (
        <Text
          variant='caption'
          numberOfLines={2}
          ellipsizeMode='tail'
          style={[
            {
              fontSize: 12, // Slightly smaller for error text
              color: danger, // Always use danger color for errors
              marginTop: 4, // Add spacing above error text
            },
          ]}
          pointerEvents='none'
        >
          {error}
        </Text>
      )}
    </View>
  );
}
```

**2.** Update the import paths to match your project setup.

## Usage

```tsx
import { Switch } from '@/components/ui/switch';
```

```tsx
<Switch
  label='Enable notifications'
  value={isEnabled}
  onValueChange={setIsEnabled}
/>
```

## Examples

#### Default

**Example:** A basic switch with label

```tsx
// components/demo/switch/switch-demo.tsx
import { Switch } from '@/components/ui/switch';
import React, { useState } from 'react';

export function SwitchDemo() {
  const [isEnabled, setIsEnabled] = useState(false);

  return (
    <Switch
      label='Enable notifications'
      value={isEnabled}
      onValueChange={setIsEnabled}
    />
  );
}
```

#### Without Label

**Example:** A switch without label text

```tsx
// components/demo/switch/switch-simple.tsx
import { Switch } from '@/components/ui/switch';
import React, { useState } from 'react';

export function SwitchSimple() {
  const [isEnabled, setIsEnabled] = useState(false);

  return <Switch value={isEnabled} onValueChange={setIsEnabled} />;
}
```

#### With Error State

**Example:** Switch with error message and styling

```tsx
// components/demo/switch/switch-error.tsx
import { Switch } from '@/components/ui/switch';
import { View } from '@/components/ui/view';
import React, { useState } from 'react';

export function SwitchError() {
  const [isEnabled, setIsEnabled] = useState(false);

  return (
    <View style={{ gap: 46 }}>
      <Switch
        label='Terms and conditions'
        value={isEnabled}
        onValueChange={setIsEnabled}
      />

      <Switch
        label='Privacy policy'
        value={false}
        onValueChange={() => {}}
        error='You must accept the privacy policy'
      />
    </View>
  );
}
```

#### Disabled State

**Example:** Switches in disabled state

```tsx
// components/demo/switch/switch-disabled.tsx
import { Switch } from '@/components/ui/switch';
import { View } from '@/components/ui/view';
import React, { useState } from 'react';

export function SwitchDisabled() {
  const [value, setValue] = useState(false);

  return (
    <View style={{ gap: 12 }}>
      <Switch value={value} label='Disabled (Off)' onValueChange={setValue} />

      <Switch
        label='Disabled (On)'
        value={true}
        onValueChange={() => {}}
        disabled={true}
      />
    </View>
  );
}
```

#### Settings List

**Example:** Multiple switches arranged in a settings list

```tsx
// components/demo/switch/switch-settings.tsx
import { Switch } from '@/components/ui/switch';
import { View } from '@/components/ui/view';
import { useColor } from '@/hooks/useColor';
import { BORDER_RADIUS } from '@/theme/globals';
import React, { useState } from 'react';

export function SwitchSettings() {
  const card = useColor('card');

  const [notifications, setNotifications] = useState(true);
  const [darkMode, setDarkMode] = useState(false);
  const [location, setLocation] = useState(true);
  const [analytics, setAnalytics] = useState(false);

  return (
    <View
      style={{
        backgroundColor: card,
        borderRadius: BORDER_RADIUS,
        padding: 16,
        gap: 16,
      }}
    >
      <Switch
        label='Push notifications'
        value={notifications}
        onValueChange={setNotifications}
      />
      <Switch label='Dark mode' value={darkMode} onValueChange={setDarkMode} />
      <Switch
        label='Location services'
        value={location}
        onValueChange={setLocation}
      />
      <Switch
        label='Analytics & performance'
        value={analytics}
        onValueChange={setAnalytics}
      />
    </View>
  );
}
```

#### Custom Colors

**Example:** Switches with custom colors and styling

```tsx
// components/demo/switch/switch-colors.tsx
import { Switch } from '@/components/ui/switch';
import { View } from '@/components/ui/view';
import React, { useState } from 'react';

export function SwitchColors() {
  const [switch1, setSwitch1] = useState(true);
  const [switch2, setSwitch2] = useState(true);
  const [switch3, setSwitch3] = useState(true);

  return (
    <View style={{ gap: 12 }}>
      <Switch
        label='Default green'
        value={switch1}
        onValueChange={setSwitch1}
      />
      <Switch
        label='Custom blue'
        value={switch2}
        onValueChange={setSwitch2}
        trackColor={{ false: '#e0e0e0', true: '#2196F3' }}
        thumbColor={switch2 ? '#ffffff' : '#f4f3f4'}
      />
      <Switch
        label='Custom purple'
        value={switch3}
        onValueChange={setSwitch3}
        trackColor={{ false: '#e0e0e0', true: '#9C27B0' }}
        thumbColor={switch3 ? '#ffffff' : '#f4f3f4'}
      />
    </View>
  );
}
```

## API Reference

### Switch

A toggle switch component with optional label and error states.

| Prop            | Type                              | Default | Description                                                    |
| --------------- | --------------------------------- | ------- | -------------------------------------------------------------- |
| `haptic`        | `boolean`                         | `true`  | Whether to trigger haptic feedback when the switch is toggled. |
| `label`         | `string`                          | -       | Optional label text for the switch.                            |
| `error`         | `string`                          | -       | Error message to display (changes label color).                |
| `labelStyle`    | `TextStyle`                       | -       | Additional styles to apply to the label text.                  |
| `value`         | `boolean`                         | -       | The current state of the switch.                               |
| `onValueChange` | `(value: boolean) => void`        | -       | Callback fired when the switch state changes.                  |
| `disabled`      | `boolean`                         | `false` | Whether the switch is disabled.                                |
| `...props`      | `SwitchProps` (from React Native) | -       | All other React Native Switch props are supported.             |

## Accessibility

The Switch component is built with accessibility in mind:

- Uses native React Native Switch for optimal platform behavior
- Supports screen reader announcements for state changes
- Proper focus management and keyboard navigation
- Color contrast meets accessibility standards
- Label text is properly associated with the switch control
- Error states provide clear feedback to assistive technologies
