Switch
Accessible, unstyled native switch with controlled and uncontrolled state.
| Package | @vizejs/ui/switch |
| Maturity | stable |
| Own the source | vize lib pull switch |
| Requires | controllable-state, id |
| Aliases | switch control, on off switch, form switch |
| Covers | WAI-ARIA Switch, React Aria Switch, Reka UI Switch |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/ui
Add the public import below to <script setup lang="ts">. Components provide behavior without styles; the preview uses optional Paper styles.
import { Switch } from "@vizejs/ui/switch";
Or copy the source into your project with vize lib pull switch (see Source Distribution).
Try it
This is the basic example maintained with the component source, using the public package imports. The preview adds the optional Paper styles.
Open the interactive example · Styling these components

Copy the example
<!-- Controlled switch bound with v-model and labelled by visible text. -->
<script setup lang="ts">
import { ref, useId } from "vue";
import { Switch } from "@vizejs/ui/switch";
const enabled = ref(false);
const labelId = useId();
</script>
<template>
<div>
<span :id="labelId">Email notifications</span>
<Switch v-model="enabled" name="notifications" :aria-labelledby="labelId" />
<output>{{ enabled ? "on" : "off" }}</output>
</div>
</template>
API
Switch
Source: src/families/selection/switch/switch-control.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
id |
string | null |
undefined |
Consumer-owned control id. null and undefined select a deterministic fallback. |
name |
string |
undefined |
Native form field name submitted while the switch is checked. |
value |
string |
"on" |
Native form value submitted while the switch is checked. |
modelValue |
boolean |
undefined |
Controlled checked value. undefined selects uncontrolled behavior. |
defaultChecked |
boolean |
false |
Initial checked state for uncontrolled use and the state restored by reset. |
disabled |
boolean |
false |
Disable activation, focus, and native form submission. |
readOnly |
boolean |
false |
Keep the switch focusable while preventing user state changes. |
required |
boolean |
false |
Mark the switch as required for assistive technology. |
ariaLabel |
string |
undefined |
Accessible name when no visible label or aria-labelledby supplies one. |
ariaLabelledby |
string |
undefined |
Space-separated ids that label the switch. |
ariaDescribedby |
string |
undefined |
Space-separated ids that describe the switch. |
ariaErrormessage |
string |
undefined |
Id of the validation error message used while invalid. |
ariaInvalid |
SwitchAriaInvalid |
false |
Invalid state announced to assistive technology. |
Events
| Event | Payload | Description |
|---|---|---|
update:modelValue |
[value: boolean] |
Fired when the checked state requests a new controlled boolean value. |
change |
[value: boolean, nativeEvent: MouseEvent] |
Fired after user activation requests the next checked value. |
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
SwitchSlotState |
Renders the switch contents with current checked and availability state. |
Exposed
| Member | Type | Description |
|---|---|---|
checked |
boolean |
Current controlled or uncontrolled checked state. |
focus |
(options?: FocusOptions) => void |
Move focus to the native switch button. |
toggle |
() => boolean |
Request the opposite checked state and report whether it differs. |
setChecked |
(value: boolean) => boolean |
Request a checked-state update and report whether it differs. |
reset |
() => boolean |
Restore the current default checked state and report whether it changed. |
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
Behavior
Normative state × input → outcome table for switch-control.vue (@vizejs/ui/switch).
Every row is proven by the named mounted-DOM test in src/families/selection/switch/switch.test.ts; a row
without a passing test is a contract violation.
| # | State | Input | Outcome | Proven by |
|---|---|---|---|---|
| S1 | named, checked | render | native <button type="button" role="switch">, deterministic id, explicit aria-checked, ARIA field state, and checked hidden form value |
renders a named native switch with ARIA and form attributes |
| S2 | uncontrolled | pointer click | toggles aria-checked, data-state, data-checked, and checked form value; emits update:modelValue before change |
uncontrolled switch toggles with pointer activation and form data |
| S3 | controlled | pointer click | emits the request and change; rendered checked state reverts to modelValue until the parent accepts the update |
controlled checked state wins until the parent accepts the request |
| S4 | uncontrolled, seeded | form reset | defaultChecked seeds the initial state and form reset restores it without request-global state |
defaultChecked seeds state and native form reset restores it |
| S5 | focusable | Enter / Space | native button keyboard activation toggles the switch from both Enter and Space | keyboard activation toggles with Enter and Space |
| S6 | disabled | click / Tab | native disabled, aria-disabled, data-state="disabled", no checked form value, no toggle, and no sequential focus |
disabled and read-only switches keep availability semantics |
| S7 | read-only | click / Tab | aria-readonly, data-state="readonly", remains focusable, preserves form value, and suppresses user toggles |
disabled and read-only switches keep availability semantics |
| S8 | uncontrolled | exposed API | toggle() and setChecked() update state, focus() focuses the button, and reset() restores the default checked state |
exposes focus, toggle, setChecked, reset, and slot state |
The subpath remains tree-shakable and retains no packaged CSS; those package
contracts are pinned by distribution.test.ts, check:size, and
check:tree-shaking.