AntaSwitch
Switch to dark theme
Search documentation
On this page

Switch (toggle)

Switches change a single setting immediately. Use a checkbox when people select one or more values to submit together.

The Switch wrapper renders a form-associated <a-switch> element. It works uncontrolled with defaultChecked, or controlled with checked and onStateChange.

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.

NeutralBrandInfoSuccessWarningCriticalCustom
<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.

Off — neutralOn — brandOn — critical
<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.

Delete workspaceThis cannot be undone.
/* 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).

SmallMediumLarge
<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.

6px round2px roundSquare
<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.

Email notificationsWeekly digest, never marketing.Automatic updatesDownloads in the background.
<Switch defaultChecked label="Email notifications" hint="Weekly digest, never marketing." />
<Switch defaultChecked label="Automatic updates" labelPosition="start" />

States

Switches are either unchecked or checked.

UncheckedChecked
<Switch label="Unchecked" />
<Switch defaultChecked label="Checked" />

Disabled

The disabled attribute dims the switch and removes it from the tab order.

DisabledDisabled checked
<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.

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:

CallbackWhenCancelablePayload
onStateChange(e, { next, prev })before the toggle appliese.preventDefault() vetoesnext / previous boolean
onChange(e)after it appliesnative 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 wrapper sets role="switch", and the element keeps aria-checked in sync with the current value. Its light-DOM label supplies the accessible name and its hint supplies the accessible description through ElementInternals; no generated IDs are needed. For a label-less switch, pass aria-label.

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 <Card>, 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 the web component directly when you are not using React or Preact and a native control does not fit.

The focusable host carries the state. Label and hint are light-DOM children that the element exposes as its accessible name and description.

Email updatesProduct and security notices.
<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.

Strong labelChecked mark on the track× mark on the track
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);
}