Switch
Primitive

Switch

Headless on/off switch with optional native form participation.

Live behavior

Switch in motion

Interactive
Atom behavior · App-owned appearance

Toggle the setting with Space or a pointer.

waiting for input

When to Use

Use Switch for a setting that becomes active or inactive immediately, such as notifications. Use Checkbox when the choice is part of a form that is applied later, and use Toggle for a pressed command like bold text.

Features

The behavior Atom owns before your product adds appearance.

  • Renders a WAI-ARIA switch.
  • Can be controlled or uncontrolled.
  • Supports disabled, read-only, required, and invalid states.
  • Renders an optional hidden checkbox input for native form submission.
  • Includes a decorative thumb part that mirrors root state.
  • Supports asChild and render.

Import

tsx
import { Switch } from "@flowstack-ui/atom";

Anatomy

tsx
<Switch.Root>
  <Switch.Thumb />
</Switch.Root>

API Reference

Root

Owns the checked and form state and renders a native button by default. It also provides state to Thumb and renders a transparent native checkbox when name or required needs native form behavior. The proxy is aligned to Root, participates in required validation without readonly, and redirects browser validation focus to the visible switch.

PropTypeDefault
checkedboolean-
defaultCheckedbooleanfalse
onCheckedChange(checked: boolean) => void-
disabledbooleanfalse
readOnlybooleanfalse
invalidbooleanfalse
requiredbooleanfalse
validationBehavior"inline" | "native"Field/Form value or "native"
namestring-
valuestring"on"
formstring-
asChildbooleanfalse
renderRenderProp-
ARIA attributeValues
role"switch"
aria-checkedCurrent checked state
aria-labelNative value when provided
aria-requiredtrue when required
aria-readonlytrue when read-only
aria-invalidtrue when invalid
Data attributeValues
[data-slot]"switch"
[data-state]"checked" | "unchecked"
[data-disabled]Present when disabled
[data-readonly]Present when read-only
[data-required]Present when required
[data-invalid]Present when invalid

Thumb

Mirrors Root state for the movable visual part while remaining hidden from assistive technology.

PropTypeDefault
asChildbooleanfalse
renderRenderProp-
ARIA attributeValues
aria-hiddenAlways true
Data attributeValues
[data-slot]"switch-thumb"
[data-state]"checked" | "unchecked"
[data-disabled]Present when disabled
[data-readonly]Present when read-only
[data-required]Present when required
[data-invalid]Present when invalid

Advanced compound parts can read useSwitchContext or use the public SwitchContextProvider.

Examples

Form Submission

tsx
import { Switch } from "@flowstack-ui/atom";

export default function NotificationSetting() {
  return <Switch.Root name="notifications" value="enabled" aria-label="Notifications"><Switch.Thumb /></Switch.Root>;
}

Controlled

tsx
import { useState } from "react";
import { Switch } from "@flowstack-ui/atom";

export default function ControlledSwitch() {
  const [enabled, setEnabled] = useState(false);
  return <Switch.Root checked={enabled} onCheckedChange={setEnabled} aria-label="Notifications"><Switch.Thumb /></Switch.Root>;
}

Accessibility

After native constraint validation runs, proxy invalidity is mirrored to the visible Root and its Field. Inline behavior suppresses the browser bubble while native behavior keeps the aligned browser UI.

Switch follows the WAI-ARIA switch pattern. Root owns the switch role and checked state; Thumb is decorative.

KeyDescription
EnterToggles checked state.
SpaceToggles checked state.

Provide visible text, native aria-label, or aria-labelledby. Inside Field, Switch inherits state, control ID, and descriptions. Uncontrolled state resets to defaultChecked. Read-only switches remain focusable but cannot toggle.

Changelog

Unreleased

0.24.0

  • Added public Agent Knowledge for component selection, required composition, recurring mistakes, and validation.

0.6.16

  • Explicitly scrolled inline validation-directed focus into view.

0.6.15

  • Exposed inline validation-directed focus through [data-focus-visible] until blur.

0.6.13

  • Mirrored aligned-proxy validity to the visible Switch, Field, and Form under the shared inline/native validation contract.

0.6.12

  • Restored native required validity by removing readonly from the aligned proxy, including required Switches without a submission name.

0.5.0

  • Added Field state, generated control ID, and description integration; removed ariaLabel in favor of native ARIA; uncontrolled state now follows native form reset.

0.2.0

  • Added readOnly support.
  • Added aria-required, data-required, data-readonly, and mirrored thumb data attributes.
  • Added keyboard activation for non-native asChild and render switch roots.
  • Memoized the compound context value.
  • Changed toggling to use functional controllable-state updates.

0.1.0

  • Initial Atom release with root, thumb, checked state, and optional form input.