Switch

On/off control with role="switch" for assistive technology, backed by a native checkbox so it still participates in HTML form submission. Same controllable checked contract as Checkbox, without indeterminate.

Preview

Usage

tsx
import { useState } from "react";
import { Switch } from "@sometic/react/selection";

export function Example() {
    const [checked, setChecked] = useState(true);
    return (
        <Switch
            checked={checked}
            onCheckedChange={setChecked}
            name="notifications"
            value="on"
            aria-label="Email notifications"
        />
    );
}
tsx
import { useState } from "react";
import { Switch } from "@sometic/react/selection";

export function Example(): JSX.Element {
    const [checked, setChecked] = useState(true);
    return (
        <Switch
            checked={checked}
            onCheckedChange={setChecked}
            name="notifications"
            value="on"
            aria-label="Email notifications"
        />
    );
}
html
<script type="module">
    import { registerSelectionElements } from "@sometic/elements/selection";
    registerSelectionElements();
</script>

<sometic-switch
    checked
    name="notifications"
    value="on"
    aria-label="Email notifications"
></sometic-switch>

Vue

vue
<script setup>
import { ref } from "vue";
import { Switch } from "@sometic/vue/selection";

const checked = ref(true);
</script>

<template>
    <Switch
        v-model:checked="checked"
        name="notifications"
        value="on"
        aria-label="Email notifications"
    />
</template>

How it works

  1. Engine (@sometic/dom/switch): resolveSwitch / createSwitchController mirror checkbox controllable state but set role="switch" and aria-checked (true / false only).
  2. Adapters: React/Vue render <input type="checkbox"> with switch attributes and bridge controlled/uncontrolled checked.
  3. Custom element: sometic-switch observes checked, disabled, name, value, shadow and emits checked-change.

Anatomy

Partdata-slotRole
RootrootNative checkbox input with switch ARIA

State / ARIA from resolve: data-checked, data-disabled, data-invalid, optional data-size / data-variant, role="switch", aria-checked, aria-invalid when invalid.

Props / attributes

React SwitchProps

Same shape as Checkbox minus indeterminate:

PropTypeDefaultDescription
checkedbooleanControlled
defaultCheckedbooleanfalseUncontrolled initial
onCheckedChange(checked: boolean) => voidChange
disabled / required / invalidbooleanfalseFlags
name / valuestringForm association
size / variantstringTheme attrs
stylingStyleableProps<"root">Unstyled contract
Native attrsremaining input attrsForwarded; ref OK

Vue

v-model:checkedchecked / update:checked; also emits checkedChange.

Custom element (sometic-switch)

Observed: checked, disabled, name, value, shadow.

Events / callbacks

SurfaceEventPayload
ReactonCheckedChangeboolean
Vueupdate:checked / checkedChangeboolean
Custom elementchecked-change{ checked: boolean }

Controlled vs uncontrolled

Same as Checkbox: pass checked (+ handler) for controlled; omit and use defaultChecked for uncontrolled.

Form participation

Posts like a checkbox when checked (name / value). Prefer Switch for settings UX; Checkbox for forms and multi-select lists. Works with native forms, Form, and sometic-form.

Accessibility

ConcernGuidance
Rolerole="switch" + aria-checked
KeyboardNative Space toggles the underlying checkbox
LabelVisible label or aria-label; do not remove the native input
Invalidaria-invalid / data-invalid when invalid
IndeterminateNot supported; use Checkbox for tri-state

Styling

Unstyled by default. Target:

  • [role="switch"]
  • [data-checked], [data-disabled], [data-invalid]
  • [data-slot="root"]
  • optional [data-size] / [data-variant]

Light DOM is the CE default; shadow opts into isolation.

Edge cases

  • Dual semantics (checkbox + switch role): intentional for form serialization + AT.
  • Controlled without handler: UI will not flip when clicked.
  • SSR: resolve is pure; register CE in the browser.
  • Multi-instance: no shared singleton state.
  • Required: native required on the input; validate group/settings with forms meta when needed.

Performance notes

Identical cost profile to Checkbox resolve. Prefer @sometic/react/selection subpath over inventing parallel switch widgets.

When to use / When not

Use for instant settings (notifications on/off, feature toggles that feel like switches).

Do not use for:

FAQ

Why a checkbox under the hood? Form serialization and keyboard without a custom widget.

Indeterminate? Not supported. Use Checkbox.

Switch vs ToggleButton? Switch is a form-like input; ToggleButton is a pressed <button>.

Invalid? Sets aria-invalid / data-invalid; pair with Field for visible error text.

CE shadow? Optional isolation via shadow.

Does React forward refs? Yes, to the underlying <input>.

v-model in Vue? Use v-model:checked.

Bundle tip? Import from @sometic/react/selection (or Vue / elements / dom matching subpaths).