Switch (toggle) Documentation has not yet been reviewed by a human.
Switches change a single setting immediately. Use a checkbox when people select one or more values to submit together.
Use defaultChecked for an initial setting, or pair checked with
onStateChange when the application controls the setting.
Playground
Tone
Switch is neutral by default. Pass tone="brand" or another tone to color the control.
tone colors the whole control: the checked track, plus the unchecked track border and thumb. Six named tones are available, or pass any CSS color for a custom tone. The label and hint stay neutral.
<Switch defaultChecked tone="neutral" label="Neutral" /><Switch defaultChecked tone="brand" label="Brand" /><Switch defaultChecked tone="critical" label="Critical" /><Switch defaultChecked tone="mediumaquamarine" label="Custom color" />Selected-only tone
To apply a tone only while selected, pass toneScope="selected" together with tone, such as tone="brand". Only the checked track is tinted; the unchecked track border and thumb stay neutral.
<Switch tone="brand" toneScope="selected" label="Off stays neutral" /><Switch defaultChecked tone="critical" toneScope="selected" label="Checked track tinted" />Coloring the text
There is no text-tone prop. To tint the label, set color on the switch with a theme-aware --text-N-{tone} token; to tint the hint, target a-switch-hint. Pair either with tone to color the whole control.
/* The class is only for the demo — use your own selector. */a-switch.toned-text { color: var(--text-1-critical); }a-switch.toned-text a-switch-hint { color: var(--text-3-critical); }Size
Three sizes scale the track, thumb, and label together: small (26×16px track), medium (30×18px, the default), and large (34×20px).
<Switch defaultChecked size="small" label="Small" /><Switch defaultChecked label="Medium" /><Switch defaultChecked size="large" label="Large" />Round or square
Switches are fully rounded by default. Pass round with a number (pixels) or CSS length to set the track radius; the thumb radius is 3px smaller. Pass round={0} for square track corners.
<Switch defaultChecked round={6} label="6px round" /><Switch defaultChecked round={2} label="2px round" /><Switch defaultChecked round={0} label="Square" />Label and hint
Pass a plain label through label, or pass richer label content as children. Add hint for a secondary line beneath. labelPosition="start" places the label and hint before the control visually while source order remains stable. For a label-less switch, pass aria-label.
<Switch defaultChecked label="Email notifications" hint="Weekly digest, never marketing." /><Switch defaultChecked label="Automatic updates" labelPosition="start" />States
Switches are either unchecked or checked.
<Switch label="Unchecked" /><Switch defaultChecked label="Checked" />Disabled
The disabled attribute dims the switch and removes it from the tab order.
<Switch disabled label="Disabled" /><Switch disabled defaultChecked label="Disabled checked" />Controlled
Uncontrolled by default: pass defaultChecked and read the value from onStateChange or at form submit. Make it controlled with checked and onStateChange to drive it from your store.
Omit checked or pass undefined to release control and retain the applied state.
Form reset and browser restoration preserve a controlled state. An uncontrolled
reset restores defaultChecked and makes later default changes apply until the
next interaction or restoration.
const [enabled, setEnabled] = useState(false)
<Switch checked={enabled} onStateChange={(_e, { next }) => setEnabled(next)} label="Enable notifications"/>onStateChange fires before the element applies a user toggle, with (event, { next, prev }). Call event.preventDefault() to veto an uncontrolled change. A controlled switch with no onStateChange is read-only.
Events
Three callbacks, in firing order:
| Callback | When | Cancelable | Payload |
|---|---|---|---|
onStateChange(e, { next, prev }) |
before the toggle applies | ✅ e.preventDefault() vetoes |
next / previous boolean |
onChange(e) |
after it applies | — | native change event |
onValueChange(e, attrs) |
after it applies | — | { checked, name, value } |
Use onStateChange to intercept a toggle or drive a controlled switch. Use onValueChange when you only need the new form value.
<Switch name="notifications" onValueChange={(_e, { checked }) => saveNotificationPreference(checked)} label="Email notifications"/>Accessibility
The label names the switch for screen readers, and the hint supplies its
description. Without a visible label, pass aria-label. Screen readers
announce the current state.
Space toggles the switch. It follows the WAI-ARIA switch pattern.
Component props
| Prop | Type | Default | Description |
|---|---|---|---|
| label? | string | — | Visible, stable label for the setting. Use children for richer label content. |
| hint? | ReactNode | — | Secondary text rendered under the label and exposed as the switch's accessible description. It does not become part of the accessible name. |
| checked? | boolean | — | Controlled checked value. In controlled mode, update this in onStateChange. |
| defaultChecked? | boolean | false | Initial checked value for an uncontrolled switch. |
| disabled? | boolean | — | Disables interaction and removes the switch from the tab order. |
| round? | booleannumberstring | — | Fully round the thumb and track. Pass a number (px) or CSS length string
for a custom track radius; the thumb radius is 3px smaller. |
| name? | string | — | Form field name. A checked switch submits value under this name. |
| value? | string | "on" | Value submitted while checked. |
| tone? | neutralbrandcriticalinfosuccesswarningstring | 'neutral' | Color of the track and thumb. A tinted tone also colors the unchecked
track border and thumb; use toneScope="selected" to color only the checked track. |
| toneScope? | allselected | 'all' | Apply tone to every state, or only while checked so the unchecked track
and thumb stay neutral. |
| size? | smallmediumlarge | 'medium' | Size variant. small=26×16px, medium=30×18px, large=34×20px. |
| labelPosition? | startend | 'end' | Put the visible label before or after the control. Grid layout changes only the visual order, preserving DOM/source order for assistive technologies. |
| onStateChange? | (event, detail) => void | — | Fired before a user toggle applies. Call event.preventDefault() to veto an
uncontrolled change; controlled consumers accept by updating checked. |
| onChange? | (event) => void | — | Native post-apply change event. |
| onValueChange? | (event, attrs) => void | — | Post-apply callback with the new form-relevant value snapshot. |
Inherited props (children, className, id, slot, style, tabIndex, title)
| Prop | Type | Default | Description |
|---|---|---|---|
| children? | ReactNode | — | Child elements. When provided, replaces the component's default label/content. |
| className? | string | — | CSS class on the component's root element (merged with the component's own
classes). Use it directly for layout and positioning — grid/flex placement,
margins, alignment — rather than wrapping the component in a <div>/<span>. |
| id? | string | — | HTML id attribute. |
| slot? | string | — | Assigns the element to a named <slot> of a parent web component (e.g.
slot="header" inside a <Dialog>, slot="footer" inside a <Dialog>). |
| style? | CSSProperties | — | Inline styles on the component's root element. Set layout/positioning here
(or via className) directly on the component instead of adding a wrapper. |
| tabIndex? | number | — | Tab order. Set to -1 to skip the element when tabbing. |
| title? | string | — | HTML title attribute — native browser tooltip on hover. |
Plus every standard DOM event handler (onClick,
onFocus, onKeyDown, …) and any
data-* or aria-* attribute — forwarded as-is to
the underlying <a-*> element.
Web Component
Use <a-switch> directly outside JSX. Set role="switch" and tabindex="0"
for keyboard access. Its label and hint supply the accessible name and
description.
<a-switch role="switch" tabindex="0" name="updates" default-state="checked"> <a-switch-label>Email updates</a-switch-label> <a-switch-hint>Product and security notices.</a-switch-hint></a-switch> Styling
Reach for the props first: tone chooses the control color, while toneScope chooses whether it colors all states or only the checked track. tone accepts any CSS color; size scales the control, label, and hint. The focus ring is the global --focus-ring.
<Switch tone="#e0457b" defaultChecked label="Custom color" /><a-switch> is light DOM. The track is ::before, the thumb is ::after, and the label / hint are a-switch-label / a-switch-hint children. Target them with plain CSS. The classes in these examples are demo hooks. Replace them with a selector you own.
a-switch.strong-label { color: var(--text-1-brand); font-weight: 700;}
/* A CSS-only check / × in the free part of the track. */a-switch.track-marks::before { display: flex; align-items: center; font-size: 10px; font-weight: 700; line-height: 1;}a-switch.track-marks:state(checked)::before { content: "✓"; justify-content: flex-start; padding-inline-start: 4px; color: var(--switch-thumb);}a-switch.track-marks:not(:state(checked))::before { content: "×"; justify-content: flex-end; padding-inline-end: 4px; color: var(--switch-track-off-stroke);}