Skip to content

Switch

A toggle switch with an optional label, built on Reka UI's Switch primitive.

Usage

vue
<script setup lang="ts">
const notifications = ref(true)
</script>

<template>
  <SSwitch v-model="notifications" label="Notifications" />
</template>

States

disabled prevents interaction and dims it, and invalid switches the track to the danger semantic role:

vue-html
<SSwitch v-model="disabledOff" disabled label="Disabled" />
<SSwitch v-model="disabledOn" disabled label="Disabled, on" />
<SSwitch v-model="invalidChoice" invalid label="You must enable this to continue" />

See Props.

Sizes

size takes sm / md / lg, scaling the track, thumb, and label together:

vue-html
<SSwitch v-model="sm" size="sm" label="Small" />
<SSwitch v-model="md" size="md" label="Medium" />
<SSwitch v-model="lg" size="lg" label="Large" />

Colors

color sets the checked-state track/thumb color - unchecked always stays the same neutral track regardless:

vue-html
<SSwitch v-model="primary" color="primary" label="Primary" />
<SSwitch v-model="success" color="success" label="Success" />
<SSwitch v-model="danger" color="danger" label="Danger" />

Loading

loading shows a spinner in the thumb - useful while an async toggle (an API call behind the switch) is in flight. It doesn't imply disabled - combine :loading="x" :disabled="x" if a busy switch shouldn't be touchable in the meantime:

vue-html
<SSwitch :model-value="enabled" :loading="loading" label="Sync with server" @update:model-value="onUpdate" />

Icons

checked-icon/unchecked-icon render inside the thumb for each state - useful when a plain on/off doesn't read as clearly as a semantic pair of icons:

vue-html
<SSwitch v-model="airplaneMode" checked-icon="hugeicons:airplane-mode" unchecked-icon="hugeicons:airplane-mode-off" label="Airplane mode" />

Description

description adds a second, muted line under the label - for a standalone toggle (a settings list, say) where wrapping every switch in its own FormField would be needless ceremony:

vue-html
<SSwitch v-model="marketing" label="Marketing emails" description="Occasional product updates and offers - unsubscribe any time." />

Forms integration

Wrap it in FormField to get id/name/invalid and aria-describedby wired up automatically - invalid falls back to FormField's own state when not set directly on the switch, and size does the same (matching RadioGroup's own inheritance).

Accessibility

Switch renders Reka UI's Switch primitive, so the accessibility semantics come from there rather than being reimplemented here: role="switch" with a boolean aria-checked (unlike Checkbox, there's no third "mixed" state). It's a single native-equivalent control, so it's just one Tab stop with no roving focus to manage, and Space toggles it - standard behavior for the ARIA switch pattern, not something to configure here.

Custom :ui

To see exactly what you'd be overriding - the current default classes for every slot and variant - here's Switch's own theme file:

Theme source "switch" not found

Props

PropTypeDefault
idstring-
namestring-
modelValueboolean-
labelstring-
descriptionstring-
disabledbooleanfalse
invalidbooleanfalse
requiredbooleanfalse
size'sm' | 'md' | 'lg''md'
color'primary' | 'neutral' | 'secondary' | 'success' | 'danger' | 'info' | 'warning''primary'
loadingbooleanfalse
checkedIconstring-
uncheckedIconstring-
uiPartial<Record<'root' | 'track' | 'thumb' | 'icon' | 'label' | 'description' | 'labelGroup', string | object>>-

Slots

SlotDescription
defaultCustom content, overrides label
descriptionCustom content, overrides description
Edit this page