---
title: Switch
description: A two-state control for turning a setting on or off.
---

<PackageInfo name="switch" />

**bun**

```bash
bun add @data-slot/switch
```

**npm**

```bash
npm install @data-slot/switch
```

**pnpm**

```bash
pnpm add @data-slot/switch
```

## Anatomy

Just a root and an optional thumb. The controller injects a visually hidden checkbox next to the root, so the switch works inside a `<label>` and submits with a form like a native one.

```html
<span data-slot="switch">
  <span data-slot="switch-thumb"></span>
</span>
```

## Examples

### Toggle a setting

<Example name="switch" />

### Disabled state

`data-disabled` blocks interaction while screen readers still report the on/off state.

<Example name="switch" variant="extra" />

## API reference

### Initialization

#### `create(scope?)`

Auto-discover and bind all switch instances in a scope (defaults to `document`).

```typescript
import { create } from "@data-slot/switch";

const controllers = create(); // Returns SwitchController[]
```

#### `createSwitch(root, options?)`

Create a controller for a specific element.

```typescript
import { createSwitch } from "@data-slot/switch";

const controller = createSwitch(element, {
  defaultChecked: true,
  name: "notifications",
  uncheckedValue: "off",
  onCheckedChange: (checked) => console.log(checked),
});
```

### Slots

#### Runtime Slots

- `switch` - Root control that toggles the checked state and receives switch semantics, keyboard handling, and form integration.
- `switch-thumb` - Optional visual thumb inside the root; receives checked and disabled state for styling.

#### Markup

```html
<span data-slot="switch">
  <span data-slot="switch-thumb"></span>
</span>
```

Use a neutral root element (`span` or `div`) when you want Base UI-style label wrapping and shadcn-like composition. The controller injects a visually hidden checkbox next to the root for form submission, label support, and native validation.

### Data Attributes

JS options take precedence over data attributes on the root element.

| Attribute | Type | Default | Description |
|-----------|------|---------|-------------|
| `data-default-checked` | `boolean` | `false` | Initial checked state |
| `data-disabled` | `boolean` | `false` | Disable user interaction and form submission |
| `data-read-only` / `data-readOnly` | `boolean` | `false` | Prevent user interaction while keeping the field enabled |
| `data-required` | `boolean` | `false` | Require a checked value |
| `data-name` | `string` | - | Form field name |
| `data-value` | `string` | native checkbox `"on"` | Submitted value when checked |
| `data-unchecked-value` / `data-uncheckedValue` | `string` | - | Submitted value when unchecked |

### Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `defaultChecked` | `boolean` | `false` | Initial checked state |
| `disabled` | `boolean` | `false` | Disable user interaction and form submission |
| `readOnly` | `boolean` | `false` | Prevent user interaction while keeping the field enabled |
| `required` | `boolean` | `false` | Require the switch to be checked for native form validation |
| `name` | `string` | - | Form field name |
| `value` | `string` | native checkbox `"on"` | Submitted value when checked |
| `uncheckedValue` | `string` | - | Submitted value when unchecked |
| `onCheckedChange` | `(checked: boolean) => void` | `undefined` | Callback when checked state changes |

### Controller

| Method/Property | Description |
|-----------------|-------------|
| `checked` | Current checked state (readonly `boolean`) |
| `toggle()` | Toggle the checked state |
| `check()` | Set checked to `true` |
| `uncheck()` | Set checked to `false` |
| `setChecked(checked)` | Set the checked state explicitly |
| `destroy()` | Remove listeners and generated inputs |

### Events

#### Outbound Events

```javascript
element.addEventListener("switch:change", (event) => {
  console.log(event.detail.checked);
});
```

#### Inbound Events

```javascript
element.dispatchEvent(
  new CustomEvent("switch:set", { detail: { checked: true } })
);
```

### Styling

#### State Attributes

The root and thumb expose presence attributes:

- `data-checked`
- `data-unchecked`
- `data-disabled`
- `data-readonly`
- `data-required`

The root also syncs:

- `role="switch"`
- `aria-checked="true|false"`
- `aria-disabled="true"` when disabled
- `aria-readonly="true"` when read-only
- `aria-required="true"` when required

#### Tailwind Example

```html
<label class="inline-flex items-center gap-3">
  <span
    data-slot="switch"
    data-size="default"
    class="data-checked:bg-primary data-unchecked:bg-input
           focus-visible:border-ring focus-visible:ring-ring/50
           shrink-0 rounded-full p-px
           focus-visible:ring-3 peer group/switch relative
           inline-flex items-center transition-all outline-none
           h-4.5 w-8"
  >
    <span
      data-slot="switch-thumb"
      class="bg-background rounded-full size-4
             data-checked:translate-x-3.5
             data-unchecked:translate-x-0
             pointer-events-none block transition-transform"
    ></span>
  </span>
  Notifications
</label>
```
