Skip to content

Switch

Stable

Turns a setting on or off, taking effect the moment it's flipped.

Anatomy

  1. 1TrackBrand-filled when on, neutral when off. Its hit area extends invisibly to 44×36px.
  2. 2ThumbSlides across on a spring when toggled, and stretches while pressed.
  3. 3LabelNames the setting, not the action. Clicking it toggles the switch too.

Installation

$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/switch.json

Usage

import { Field, FieldLabel } from "@/components/ui/field"
import { Switch } from "@/components/ui/switch"
<Field orientation="horizontal">
  <Switch id="auto-deploy" />
  <FieldLabel htmlFor="auto-deploy">Deploy on every push</FieldLabel>
</Field>

The thumb travels on a spring and stretches while pressed, the way a physical toggle gives under your finger. With reduced motion it simply snaps into place.

Examples

Switch or checkbox?

A switch is a light switch: flipping it changes something immediately — no Save button, no confirmation. A checkbox is a form answer: nothing happens until the form is submitted. Here the switch restyles the card the instant it changes.

Night shiftStarts at 22:00
Active
Rule of thumb
If the change needs a Save or Submit button to take effect, use a Checkbox. If people would expect to see the result right away, use a switch.

Settings list

The most common home for switches: one setting per row, the label and a short description on the left, the switch on the right where the eye lands after reading. Separators keep long lists scannable.

Sizes

size="sm" fits dense tables and compact panels; the default size suits forms and settings pages.

States

Disabled switches keep their position so people can still read the current value. When a setting is required, mark the switch aria-invalid and explain why in a FieldError.

Guidelines

When to use

  • For settings that apply immediately: notifications, integrations, feature toggles, dark mode.
  • For binary states people understand without extra explanation — on or off.

When not to use

Labels

Label the setting, not the state. The switch already shows on and off, so the label should name what is being turned on — never Enable / Disable, and never text that changes when the switch flips.

Email notifications

Do.The label names the setting; the switch shows its state.

Notifications are on — click to turn off

Don’t.A label that flips with the value makes the state impossible to read.

Feedback

Because a switch acts immediately, confirm the result when it isn't visible on screen — a Toast such as Preview comments enabled — and undo the change (with an explanation) if the request fails.

Accessibility

The switch renders role="switch" with aria-checked, plus a hidden checkbox input for forms. A sibling FieldLabel with htmlFor both names it and toggles it on click.

KeyBehavior
Tab
Moves focus to the switch. Focus shows a 2px ring offset from the track.
SpaceEnter
Toggles the switch.
  • Boundaries. The off track uses the input token, which meets 3:1 against the page (WCAG 1.4.11); the on track uses the brand solid. The thumb's position — not color alone — carries the state.
  • Target size. The 36×20px track has a transparent hit area extending it to 44×36px.
  • Motion. The spring and stretch are removed under prefers-reduced-motion.

API reference

Switch

Renders a <span role="switch"> with a thumb and a hidden <input type="checkbox">. Accepts every prop of Base UI's Switch.Root.

PropTypeDefault
size

Track size: 28×16px or 36×20px.

"sm" | "default""default"
checked

Whether the switch is on. Use with onCheckedChange when controlled.

booleanNo default
defaultChecked

The initial state when uncontrolled.

booleanfalse
onCheckedChange

Called when the switch is toggled — apply the setting here.

(checked: boolean, details) => voidNo default
name

Submits the switch with a form.

stringNo default
value

The submitted value when on.

stringNo default
uncheckedValue

The submitted value when off.

stringNo default
required

Requires the switch to be on before submitting.

booleanfalse
disabled

Ignores input and fades the control.

booleanfalse
readOnly

Shows the state but prevents changing it.

booleanfalse
aria-invalid

Shows the error state on the off track.

booleanNo default