# Switch

Turn a setting on or off.

Switch with label

[Complete TSX file](/examples/switch/default.tsx)

```tsx
"use client";

import { Label } from "@/siteplane/components/label";
import { Switch } from "@/siteplane/components/switch";

export default function Example() {
  return (
    <Label>
      <Switch />
      Marketing emails
    </Label>
  );
}
```

## Installation

Complete [one-time setup](/docs/installation/) first.

```bash
pnpm dlx shadcn@4.18.0 add @siteplane/switch
```

With npm: `npx shadcn@4.18.0 add @siteplane/switch`. [Registry JSON](/registry/switch.json) lists source files and dependencies.

For this page’s examples, also install:

```bash
pnpm dlx shadcn@4.18.0 add @siteplane/button @siteplane/form @siteplane/input @siteplane/label
```

Field is installed with Input.

## Usage

```tsx
import { Label } from "@/siteplane/components/label";
import { Switch } from "@/siteplane/components/switch";

<Label>
  <Switch />
  Marketing emails
</Label>;
```

## With description

Place a `FieldLabel` and a `FieldDescription` before the switch, inside a `Field`. The Field connects both to the switch, which sits at the end of the row, centered on it.

Switch with description

[Complete TSX file](/examples/switch/with-description.tsx)

```tsx
"use client";

import {
  Field,
  FieldDescription,
  FieldLabel,
} from "@/siteplane/components/field";
import { Switch } from "@/siteplane/components/switch";

export default function Example() {
  return (
    <Field className="w-full max-w-sm flex-row items-center justify-between gap-4">
      <div className="grid gap-1">
        <FieldLabel>Auto-save</FieldLabel>
        <FieldDescription>
          Save changes to documents as you type.
        </FieldDescription>
      </div>
      <Switch defaultChecked />
    </Field>
  );
}
```

## Disabled and invalid

`disabled` blocks interaction and leaves the value out of form data. `aria-invalid` only applies the invalid style; it does not validate.

Disabled and invalid switches

[Complete TSX file](/examples/switch/states.tsx)

```tsx
"use client";

import { Label } from "@/siteplane/components/label";
import { Switch } from "@/siteplane/components/switch";

export default function Example() {
  return (
    <div className="grid gap-3">
      <Label>
        <Switch disabled />
        Usage analytics
      </Label>
      <Label>
        <Switch defaultChecked disabled />
        Security alerts
      </Label>
      <Label>
        <Switch aria-invalid />
        Accept terms and conditions
      </Label>
    </div>
  );
}
```

## Card style

Wrap the text and the switch in `Label variant="card"` so the whole card toggles the setting. Label owns the shared hover, checked and disabled styles.

Switch card

[Complete TSX file](/examples/switch/card-style.tsx)

```tsx
"use client";

import { Label } from "@/siteplane/components/label";
import { Switch } from "@/siteplane/components/switch";

export default function Example() {
  return (
    <Label
      variant="card"
      className="flex w-full max-w-sm items-center justify-between"
    >
      <span className="flex flex-col gap-1">
        <span>Enable notifications</span>
        <span className="text-muted-foreground text-xs">
          You can enable or disable notifications at any time.
        </span>
      </span>
      <Switch defaultChecked size="sm" />
    </Label>
  );
}
```

## Controlled state

Pass `checked` with `onCheckedChange` when your application owns the value; otherwise use `defaultChecked`.

Controlled switch

[Complete TSX file](/examples/switch/controlled.tsx)

```tsx
"use client";

import { useState } from "react";
import { Label } from "@/siteplane/components/label";
import { Switch } from "@/siteplane/components/switch";

export default function Example() {
  const [checked, setChecked] = useState(false);
  return (
    <Label>
      <Switch checked={checked} onCheckedChange={setChecked} />
      Show completed tasks
    </Label>
  );
}
```

## Form integration

Inside a `Field`, the switch uses the field’s `name` and submits its `value` (`on` when none is set) only while it is on, unless you set `uncheckedValue`.

Switch in a form

[Complete TSX file](/examples/switch/form.tsx)

```tsx
"use client";

import { Button } from "@/siteplane/components/button";
import { Field, FieldLabel } from "@/siteplane/components/field";
import { Form } from "@/siteplane/components/form";
import { Switch } from "@/siteplane/components/switch";

export default function Example() {
  return (
    <Form
      className="grid w-full max-w-sm gap-4"
      onSubmit={(event) => {
        event.preventDefault();
      }}
    >
      <Field name="marketing">
        <FieldLabel>
          <Switch defaultChecked />
          Enable marketing emails
        </FieldLabel>
      </Field>
      <Button type="submit">Save preferences</Button>
    </Form>
  );
}
```

## Sizes

Omit `size` to inherit the provider’s control size.

Switch sizes

[Complete TSX file](/examples/switch/sizes.tsx)

```tsx
"use client";

import { Label } from "@/siteplane/components/label";
import { Switch } from "@/siteplane/components/switch";

const sizes = [
  ["sm", "Small"],
  ["md", "Medium"],
  ["lg", "Large"],
  ["xl", "Extra large"],
] as const;

export default function Example() {
  return (
    <div className="flex flex-wrap gap-4">
      {sizes.map(([size, label]) => (
        <Label key={size}>
          <Switch defaultChecked size={size} />
          {label}
        </Label>
      ))}
    </div>
  );
}
```

## API reference

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| size | sm \| md \| lg \| xl | provider / lg | Control dimensions. |
| children | ReactNode | unset | Rendered only for menu-item; the control supplies its own thumb. |
| checked / defaultChecked | boolean | unset / false | Controlled state or initial state. |
| onCheckedChange | (checked, eventDetails) => void | unset | Called when the user changes the checked state. |
| disabled | boolean | false | Prevents interaction. |
| name / value | string | unset | Native form participation. An on switch submits value (on if value is unset). |
| uncheckedValue | string | unset | Value submitted while the switch is off. |

`SwitchProps` describes the size extension; `SwitchPrimitive` is the Base UI namespace. Other props, including `required`, `readOnly`, `render` and `inputRef`, follow [Base UI Switch](https://base-ui.com/react/components/switch). Styled className is a string.

`switchVariants({ size, className })` returns the classes of the switch track that menu rows draw, with `lg` size; `switchThumbClassName` and `switchMenuTrackClassName` export the matching thumb and track composition strings. The standalone control takes its look from the kit stylesheet, and so does the edge of every track: a menu row's track carries `data-slot="switch-track"` and gets the standalone control's 3:1 edge. They do not create switch behavior.

[Read the installed source](/source/switch).

## Usage notes

Space changes the focused control. Supply a visible associated Label or an accessible name. Use [Checkbox Group](/docs/components/checkbox-group/) for multiple related choices and [Radio Group](/docs/components/radio-group/) for exactly one choice. Switch has no indeterminate state.

The thumb rests at the inline start of the track and travels toward its end when the switch turns on, so in a right-to-left page it starts on the right and moves left, within the track. The same holds for the switch rows of menus.
