---
title: Switch
description: A control that indicates whether a setting is on or off.
links:
  doc: https://base-ui.com/react/components/switch
  anatomy: https://base-ui.com/react/components/switch#anatomy
  api: https://base-ui.com/react/components/switch#api-reference
---

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

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

## Installation

<ComponentSource name="switch" title="components/ui/switch.tsx" />

## Usage

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

```tsx
<Switch />
```

## Examples

### Sizes

Three sizes are available.

- **sm**: Small size switch. (`--spacing(3)` / 12px)
- **default**: Default size switch. (`--spacing(4)` / 16px)
- **lg**: Large size switch. (`--spacing(5)` / 20px)

You can customize the size more by overriding the `--thumb-size` **CSS** variable. For example, you can set `--thumb-size: 1.5rem` to make the thumb size 24px.

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

export function SwitchDemo() {
  return (
    <div className="flex flex-col gap-5">
      <Label>
        <Switch size="sm" />
        Small size
      </Label>
      <Label>
        <Switch />
        Default size
      </Label>
      <Label>
        <Switch size="lg" />
        Large size
      </Label>
      <Label>
        <Switch className="[--thumb-size:--spacing(6)]" />
        Custom size
      </Label>
    </div>
  );
}
```

### Disabled

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

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

### Invalid

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

export function SwitchDemo() {
  return (
    <Label>
      <Switch aria-invalid />
      Marketing emails
    </Label>
  );
}
```

### With description

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

export function SwitchDemo() {
  return (
    <div className="flex items-start gap-2">
      <div className="flex flex-col gap-1">
        <Label htmlFor="marketing-emails">Marketing emails</Label>
        <p className="text-muted-foreground text-xs">
          By enabling marketing emails, you agree to receive emails.
        </p>
      </div>
      <Switch defaultChecked id="marketing-emails" />
    </div>
  );
}
```

### Card style

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

export function SwitchDemo() {
  return (
    <Label
      className="flex items-start gap-6 rounded-lg border p-3 hover:bg-accent/50 has-data-checked:border-primary/48 has-data-checked:bg-accent/50"
      htmlFor="enable-notifications"
    >
      <div className="flex flex-col gap-1">
        <p>Enable notifications</p>
        <p className="text-muted-foreground text-xs">
          You can enable or disable notifications at any time.
        </p>
      </div>
      <Switch className="[--thumb-size:--spacing(3.5)]" defaultChecked id="enable-notifications" />
    </Label>
  );
}
```

### Controlled

```tsx
"use client";

import { useState } from "react";

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

export function SwitchDemo() {
  const [isEmailEnabled, setIsEmailEnabled] = useState(false);
  return (
    <Label className="flex flex-col items-start gap-2">
      <Switch checked={isEmailEnabled} onCheckedChange={setIsEmailEnabled} />
      Enable marketing emails: {isEmailEnabled ? "Yes" : "No"}
    </Label>
  );
}
```

### Form integration

For demonstrating invalid state, the default value is set to `undefined`. Without toggling the switch, the switch will be in an invalid state when the form is submitted.

```tsx
"use client";

import { Controller, useForm } from "react-hook-form";
import { z } from "zod";

import { zodResolver } from "@hookform/resolvers/zod";

import { Button } from "@/components/ui/button";
import { Field, FieldError, FieldLabel } from "@/components/ui/field";
import { Switch } from "@/components/ui/switch";

const formSchema = z.object({
  marketing: z.boolean(),
});

type FormValues = z.infer<typeof formSchema>;

export function SwitchDemo() {
  const form = useForm<FormValues>({
    resolver: zodResolver(formSchema),
    defaultValues: { marketing: undefined },
  });

  const onSubmit = async (data: FormValues) => {
    alert(`Form submitted with data: ${JSON.stringify(data, null, 2)}`);
  };

  return (
    <form onSubmit={form.handleSubmit(onSubmit)} className="flex w-full max-w-sm flex-col gap-4">
      <Controller
        name="marketing"
        control={form.control}
        render={({
          field: { name, value, onChange, ...fieldProps },
          fieldState: { invalid, isTouched, isDirty, error },
        }) => (
          <Field name={name} invalid={invalid} touched={isTouched} dirty={isDirty}>
            <div className="flex w-full items-center justify-between">
              <FieldLabel>Marketing Emails</FieldLabel>
              <Switch checked={value} onCheckedChange={onChange} {...fieldProps} />
            </div>
            <FieldError match={!!error}>{error?.message}</FieldError>
          </Field>
        )}
      />
      <Button type="submit" disabled={form.formState.isSubmitting}>
        {form.formState.isSubmitting ? "Submitting..." : "Submit"}
      </Button>
    </form>
  );
}
```
