---
title: Switch
description: A toggle switch component for binary on/off states with accessible keyboard support and animated transitions
---

```tsx
'use client';

import { useState } from 'react';
import { Switch } from '@/ui/forms/Switch';

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

  return (
    <div className="flex items-center gap-8">
      <Switch
        aria-label="Enable notifications"
        checked={isEnabled}
        onChange={(_, checked) => setIsEnabled(checked)}
      />
      <span className="text-b2 font-semibold">Enable notifications</span>
    </div>
  );
}
```

## Installation

Using the CLI:

```bash
npx shadcn@latest add https://develop.trident-ui.pro.clubmed/r/switch.json
```

Or manually, copy the following code into `/ui/forms/switch.tsx`:

```tsx
'use client';

import clsx from 'clsx';
import { type ComponentPropsWithoutRef, useId } from 'react';

import { getBgColor } from '../helpers/colors/colors';
import type { Colors } from '../types/Colors';

export interface SwitchProps
  extends Omit<
    ComponentPropsWithoutRef<'input'>,
    'size' | 'type' | 'defaultChecked' | 'onChange' | 'value'
  > {
  checked: boolean;
  color?: Colors;
  value?: string | number | boolean;
  onChange?: (event: Event, value: boolean) => void;
}

export function Switch({
  id,
  name,
  checked,
  className,
  color = 'saffron',
  onChange,
  disabled = false,
  tabIndex = 0,
  children,
  role = 'switch',
  value,
  ...attrs
}: SwitchProps) {
  const internalId = useId();
  const inputId = id ?? internalId;
  const inputName = name ?? inputId;
  const hasChildren = children !== undefined && children !== null;
  const rootClassName = clsx('inline-flex items-center relative', {
    'gap-4': hasChildren,
    'cursor-pointer': !disabled,
    'cursor-not-allowed': disabled,
  });
  const Node = hasChildren ? 'label' : 'span';

  return (
    <Node htmlFor={inputId} className={rootClassName}>
      <input
        {...attrs}
        id={inputId}
        name={inputName}
        checked={checked}
        data-name="Switch"
        disabled={disabled}
        role={role}
        tabIndex={tabIndex}
        type="checkbox"
        value={typeof value === 'boolean' ? String(value) : value}
        className="absolute opacity-0 left-0 w-full top-0 m-0 p-0 z-1 h-full"
        onChange={(e) => {
          onChange?.(e.nativeEvent, e.target.checked);
        }}
      />

      <span
        className={clsx(
          className,
          'rounded-pill inline-flex min-w-56 items-center p-4 align-middle transition-colors',
          { 'bg-middleGrey': !checked },
          { [getBgColor(color)]: checked },
        )}
      >
        <svg className="size-24" viewBox="0 0 24 24">
          <g>
            <circle cx={12} cy={12} r={12} fill="var(--color-white)" />
            <circle cx={12} cy={12} r={4} fill="var(--color-middleGrey)" />
            <path d="M18.1 7.5c.4.4.4 1.08 0 1.48l-6.83 7q-.3.3-.72.3a1 1 0 0 1-.72-.3L5.9 11.96c-.4-.41-.4-1.07 0-1.48a1 1 0 0 1 1.44 0l3.2 3.27 6.12-6.25a1 1 0 0 1 .72-.3 1 1 0 0 1 .72.3" />
          </g>
        </svg>
      </span>
      {children}
    </Node>
  );
}
```

## Usage

```tsx showLineNumbers
'use client';

import { useState } from 'react';
import { Switch } from '@/docs/lib/ui/forms/switch';

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

  return (
    <div className="flex items-center gap-3">
      <Switch
        checked={isEnabled}
        onClick={() => setIsEnabled(!isEnabled)}
        color="saffron"
      />
      <span>Enable notifications</span>
    </div>
  );
}
```

## API Reference

### Switch

| Prop        | Type                                        | Default     | Description                                                                                                                                                                                           |
| ----------- | ------------------------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `checked`   | `boolean`                                   | `undefined` | The controlled checked state of the switch                                                                                                                                                            |
| `onClick`   | `MouseEventHandler<HTMLButtonElement>`      | `undefined` | Click handler called when switch is clicked (not called when disabled)                                                                                                                                |
| `color`     | `Colors`                                    | `'saffron'` | Color theme for the switch when checked. Valid values: `saffron`, `black`, `darkGrey`, `green`, `lavender`, `lightGrey`, `lightSand`, `marygold`, `orange`, `red`, `sand`, `sienna`, `ultramarine`, `verdigris`, `wave`, `white`, `current` |
| `disabled`  | `boolean`                                   | `false`     | When true, disables the switch and prevents interaction                                                                                                                                               |
| `className` | `string`                                    | `undefined` | Custom CSS classes applied to the button element                                                                                                                                                      |

The component also supports all standard button attributes via `ComponentPropsWithoutRef<'button'>`.

## Notes

- **Button-based component**: The Switch is implemented as a `<button>` element with `role="switch"` for proper accessibility
- **No label text support**: The Switch does not render children. Wrap it in an external container with label text if needed (see Usage example above)
- **Controlled component**: The `checked` prop determines the switch state - toggle it in your `onClick` handler
- **Disabled state**: When disabled, the `onClick` handler is not called, preventing interaction
- **Background colors**:
  - Unchecked: `bg-middleGrey`
  - Checked: Uses the `color` prop (e.g., `bg-saffron`, `bg-green`)
- **Fixed size**: `min-w-56` (56px min-width) with `p-4` padding (4px) - size is not customizable
- **Visual structure**: Displays an SVG icon containing:
  - White circle (24px diameter)
  - Grey center dot (middleGrey, 4px radius)
  - Checkmark path (visible in both states)
- **Accessibility**:
  - Uses `role="switch"` for proper ARIA semantics
  - Sets `aria-checked` attribute to indicate state to assistive technologies
  - Supports keyboard navigation (space/enter to activate)
- **Data attributes**: Sets `data-name="Switch"` for testing and debugging
- **Client component**: Requires `'use client'` directive when used in React Server Components
