A React switch with a Liquid Glass thumb, label, description, and monochrome theme.
Switch renders a two-state control with a Liquid Glass track and thumb. Use it for settings that take effect immediately; for a single action that stays pressed, use Toggle.
The switch below is the real component and can be clicked directly.
The switch root is the outermost element. The visual track is the inner base
slot, and the whole row, including the label and description, is clickable. label becomes the accessible name through aria-labelledby; when
the control has a label or explicit name, description is linked through
aria-describedby. An explicit aria-label or aria-labelledby takes
precedence for the name, and an explicit aria-describedby is merged with the
description. A description-only control uses the description as its name and
does not add aria-describedby.
Supports xs / sm / md / lg / xl, defaulting to md. The track and text scale together with the size.
checked and onCheckedChange let external state fully control the switch:
Without checked, Switch owns its state, and defaultChecked only sets the initial value:
Switch forwards name and value and submits the current value through a hidden checkbox input:
Switch renders a focusable element with role="switch" and aria-checked (a <span> by default). The visible label names it and description is linked with aria-describedby; explicit ARIA names take precedence.
Space toggles the focused control. The hidden checkbox carries name and value for form submission, and readOnly prevents changes while keeping focus. See the accessibility guide for form patterns.
| Prop | Type | Default | Description |
|---|---|---|---|
label | ReactNode | — | Visible label. It becomes the accessible name unless aria-label or aria-labelledby is provided. |
description | ReactNode | — | Supporting text, linked with aria-describedby when the control has a name. |
size | "xs" | "sm" | "md" | "lg" | "xl" | "md" | Controls track size and text size. |
checked | boolean | — | Controlled checked state. |
defaultChecked | boolean | false | Initial checked state when uncontrolled. |
onCheckedChange | (checked: boolean, details) => void | — | Called when the checked state changes. |
disabled | boolean | false | Disables the switch. |
readOnly | boolean | false | Prevents toggling while remaining focusable. |
required | boolean | false | Requires the switch to be on for form submission. |
name | string | — | Form field name. |
form | string | — | ID of the form that owns the hidden input. |
value | string | — | Value submitted with the form when the switch is on. |
uncheckedValue | string | — | Value submitted with the form when the switch is off. |
id | string | — | ID of the hidden input, or of the root element when nativeButton is true. |
inputRef | Ref<HTMLInputElement> | — | Ref to the hidden native input. |
nativeButton | boolean | false | Whether the rendered element is a native <button>. |
render | ReactElement | ((props, state) => ReactElement) | — | Replaces the root element. |
ref | Ref<HTMLElement> | — | Ref forwarded to the root element. |
className | string | — | Tailwind classes merged into the switch root. |
style | CSSProperties | — | Inline styles merged into the same element as className. |
ui | { root?: ClassValue; container?: ClassValue; base?: ClassValue; thumb?: ClassValue; wrapper?: ClassValue; label?: ClassValue; description?: ClassValue } | — | Tailwind classes for internal slots, keyed by data-slot. |
| Attribute | Description |
|---|---|
data-checked | Present when the switch is on. |
data-unchecked | Present when the switch is off. |
data-disabled | Present when the switch is disabled. |
data-readonly | Present when the switch is read-only. |
data-required | Present when the switch is required. |
data-valid | Present when the switch is valid and wrapped in Field. |
data-invalid | Present when the switch is invalid and wrapped in Field. |
data-touched | Present after the switch is touched while wrapped in Field. |
data-dirty | Present after the switch value changes while wrapped in Field. |
data-filled | Present when the switch is on while wrapped in Field. |
data-focused | Present when the switch is focused while wrapped in Field. |
data-animating | Present while the thumb transition is running. |
data-slot | Marks internal parts (root, container, base, thumb, wrapper, label, description) for styling. |
The track and thumb use --ui-ink and --ui-surface. Component-specific geometry lives in src/theme/switch.ts as --ui-switch-width, --ui-switch-height, --ui-switch-inset, --ui-switch-thumb-size, and --ui-switch-travel. Override them with style on the Switch, use className for layout, and use ui for internal slots keyed by data-slot. See the theming guide for token layers and presets.