Switch
A switch turns a setting on or off, and takes effect straight away. It can also be disabled, on or off.
import { Label } from "@/components/ui/label";
import { Switch } from "@/components/ui/switch";
export default function SwitchDemo() {
return (
<>
<Label>
<Switch defaultChecked />
Wi-Fi
</Label>
<Label>
<Switch />
Bluetooth
</Label>
<Label>
<Switch disabled />
Location
</Label>
<Label>
<Switch disabled defaultChecked />
Sync
</Label>
</>
);
}The code
The component is a single file, read from the repository when the site is built. Press Show code to see all of it.
import type { ComponentProps } from "react";
import { Switch as BaseSwitch } from "@base-ui/react/switch";
import { cx } from "@/lib/cx";
import { focusRing } from "@/lib/styles";
// On is the accent; off is the strong border color. The thumb is light on the off track and dark
// on the green one. A disabled switch goes flat and grey but still shows which way it is set.
const root = [
"inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full bg-border-strong p-0.5",
"transition-colors duration-150",
focusRing,
"not-data-[disabled]:data-[checked]:bg-accent",
"data-[disabled]:cursor-not-allowed data-[disabled]:bg-surface data-[disabled]:ring-1 data-[disabled]:ring-border data-[disabled]:ring-inset",
].join(" ");
const thumb = [
"block size-5 rounded-full bg-text",
"transition-[translate,background-color] duration-150",
"data-[checked]:translate-x-5 not-data-[disabled]:data-[checked]:bg-bg",
"data-[disabled]:bg-text-secondary",
].join(" ");
export function Switch({
className,
...props
}: Omit<ComponentProps<typeof BaseSwitch.Root>, "className"> & { className?: string }) {
return (
<BaseSwitch.Root className={cx(root, className)} {...props}>
<BaseSwitch.Thumb className={thumb} />
</BaseSwitch.Root>
);
}Props
| Prop | Type | Default | What it does |
|---|---|---|---|
defaultChecked | boolean | false | Whether it starts on, when you let it manage its own state. |
checked | boolean | — | Whether it is on, when you control it yourself. |
onCheckedChange | function | — | Called with the new value when the switch is flipped. |
disabled | boolean | false | Turns the switch off. It keeps showing which way it was set. |
className | string | — | Extra classes, added after the switch's own. |
Every other prop, such as name, goes to the Base UI Switch underneath.
The thinking
A switch and a checkbox look alike but promise different things, and the difference matters to the person using them.
- Immediate or later: Use a switch for a setting that applies the moment it flips, like Wi-Fi. Use a checkbox for a choice that is submitted later with a form.
- Built on Base UI: It is announced as a switch, with the on or off state, and flips with the keyboard.
- Green means on: The accent fills the track when the switch is on, so the state reads at a glance.
Interface design
- Size: A pill 44px wide and 24px tall, with a 20px thumb.
- Off: The strong border color for the track and a light thumb, 8.2:1 against the track.
- On: An accent track and a dark thumb, 14.6:1 against it.
- Disabled: A flat track with a hairline edge and a grey thumb. The thumb still sits left or right, so you can see how it is set.
Interaction
- Flip it: Click it, click its label's text, or press Space when it has focus.
- Motion: The thumb slides across and the track changes color over 150ms.
- Focus: Keyboard focus draws a 2px outline, 2px away from the pill.
- Disabled: It doesn't flip, and shows a not-allowed cursor.
Empathy
- Name the setting, not the state: Label it Wi-Fi, not On. The switch itself already says whether it is on.
- Announced: A screen reader says the label, that it is a switch, and whether it is on or off.
- Don't rely on color: The thumb's position shows the state as well as the track color.
- Target size: 44px wide and 24px tall, which meets the 24px minimum of WCAG 2.2 AA.