Skip to main content

Switch

A headless toggle switch component that provides accessible, flexible on/off switching functionality. Supports standalone boolean state or multi-selection within groups, similar to checkboxes but with distinct toggle UI semantics.

Features

  • Dual-mode operation: Standalone with v-model or group multi-selection
  • Visual components: Separate Track and Thumb components for flexible styling
  • Tri-state support: On, off, and indeterminate states (in group mode)
  • Full accessibility: Uses role="switch" with proper ARIA attributes
  • Form integration: Automatic hidden input generation for native form submission
  • Select all: Built-in component for bulk toggling in groups
  • Flexible rendering: Works with any element via as prop or renderless mode

Basic Usage

<script setup lang="ts">
import { Switch } from '@vuetify/v0'
import { ref } from 'vue'

const enabled = ref(false)
</script>

<template>
  <label>
    <Switch.Root v-model="enabled">
      <Switch.Track>
        <Switch.Thumb />
      </Switch.Track>
    </Switch.Root>
    Enable notifications
  </label>
</template>

Switch Group

<script setup lang="ts">
import { Switch } from '@vuetify/v0'
import { ref } from 'vue'

const features = ref<string[]>([])
</script>

<template>
  <Switch.Group v-model="features">
    <label>
      <Switch.Root value="dark-mode">
        <Switch.Track>
          <Switch.Thumb />
        </Switch.Track>
      </Switch.Root>
      Dark mode
    </label>
    <label>
      <Switch.Root value="auto-save">
        <Switch.Track>
          <Switch.Thumb />
        </Switch.Track>
      </Switch.Root>
      Auto-save
    </label>
    <label>
      <Switch.Root value="notifications">
        <Switch.Track>
          <Switch.Thumb />
        </Switch.Track>
      </Switch.Root>
      Notifications
    </label>
  </Switch.Group>
</template>

Components

Switch.Root

The main switch component. Supports standalone mode with v-model or group mode within Switch.Group.
modelValue
boolean
default:"false"
The on/off state (standalone mode only)
value
unknown
Value associated with this switch (used in group mode and form submission)
name
string
Form field name. When provided, automatically renders a hidden input for form submission
disabled
boolean | Ref<boolean>
default:"false"
Disables the switch, preventing interaction
indeterminate
boolean | Ref<boolean>
default:"false"
Sets the indeterminate/mixed state (standalone mode)
label
string
Optional display label (passed to slot props)
id
string
Unique identifier (auto-generated if not provided)
form
string
Associate with a form element by ID
as
string
default:"'button'"
HTML element to render as
renderless
boolean
default:"false"
Render only slot content without wrapper element
namespace
string
default:"'v0:switch:root'"
Context namespace for child components
groupNamespace
string
default:"'v0:switch:group'"
Namespace for connecting to parent group

Slot Props

isChecked
boolean
Whether the switch is on
isMixed
boolean
Whether the switch is in indeterminate state
isDisabled
boolean
Whether the switch is disabled
select
() => void
Function to turn the switch on
unselect
() => void
Function to turn the switch off
toggle
() => void
Function to toggle switch state
attrs
object
Pre-computed ARIA and data attributes including role="switch" and aria-checked

Switch.Group

Container for managing multiple switches with multi-selection support.
modelValue
T[]
default:"[]"
Array of enabled switch values
disabled
boolean
default:"false"
Disables all switches in the group
enroll
boolean
default:"false"
Auto-enable all non-disabled switches on mount
mandatory
boolean | 'force'
default:"false"
  • false: No enforcement
  • true: Prevents disabling the last enabled switch
  • 'force': Auto-enables first non-disabled switch
label
string
Accessible name for the group
as
string
default:"'div'"
HTML element to render as
renderless
boolean
default:"false"
Render only slot content without wrapper
namespace
string
default:"'v0:switch:group'"
Context namespace for child switches

Slot Props

isDisabled
boolean
Whether the group is disabled
isNoneSelected
boolean
Whether no switches are enabled
isAllSelected
boolean
Whether all selectable switches are enabled
isMixed
boolean
Whether some but not all switches are enabled
selectAll
() => void
Enable all non-disabled switches
unselectAll
() => void
Disable all switches
toggleAll
() => void
Toggle between all enabled and none enabled

Switch.Track

The background track/rail of the switch. Always visible, provides styling container for the thumb.
as
string
default:"'span'"
HTML element to render as
renderless
boolean
default:"false"
Render only slot content without wrapper
namespace
string
default:"'v0:switch:root'"
Context namespace to inject from parent

Slot Props

isChecked
boolean
Whether the switch is on
isMixed
boolean
Whether the switch is indeterminate
attrs
object
Contains data-state for CSS styling

Switch.Thumb

The sliding knob/button of the switch. Position indicates on/off state.
as
string
default:"'span'"
HTML element to render as
renderless
boolean
default:"false"
Render only slot content without wrapper
namespace
string
default:"'v0:switch:root'"
Context namespace to inject from parent

Slot Props

isChecked
boolean
Whether the switch is on
isMixed
boolean
Whether the switch is indeterminate
attrs
object
Contains data-state for CSS styling
Unlike Checkbox.Indicator, the Switch.Thumb is always visible. Use CSS transitions on data-state to animate its position.

Switch.SelectAll

Special switch for enabling/disabling all switches in a group. Must be used within Switch.Group.
label
string
Accessible label for the select all switch
disabled
boolean | Ref<boolean>
default:"false"
Disables the select all switch
as
string
default:"'button'"
HTML element to render as
renderless
boolean
default:"false"
Render only slot content without wrapper
namespace
string
default:"'v0:switch:root'"
Context namespace for child components
groupNamespace
string
default:"'v0:switch:group'"
Namespace for connecting to parent group

Slot Props

isAllSelected
boolean
Whether all switches are enabled
isMixed
boolean
Whether some but not all switches are enabled
isDisabled
boolean
Whether the select all is disabled
selectAll
() => void
Enable all switches
toggleAll
() => void
Toggle all switches

Switch.HiddenInput

Hidden input for native form submission. Automatically rendered when name prop is provided to Switch.Root.
name
string
Form field name (required)
value
unknown
Value to submit when enabled (overrides context value)
form
string
Associate with form by ID (overrides context form)
namespace
string
default:"'v0:switch:root'"
Context namespace to inject from parent

Advanced Examples

Styled Switch with Animation

<script setup lang="ts">
import { Switch } from '@vuetify/v0'
import { ref } from 'vue'

const enabled = ref(false)
</script>

<template>
  <Switch.Root v-model="enabled" class="switch-root">
    <Switch.Track class="switch-track">
      <Switch.Thumb class="switch-thumb" />
    </Switch.Track>
  </Switch.Root>
</template>

<style>
.switch-root {
  position: relative;
  display: inline-block;
}

.switch-track {
  display: block;
  width: 44px;
  height: 24px;
  background: #ccc;
  border-radius: 24px;
  transition: background 0.2s;
}

.switch-track[data-state="checked"] {
  background: #4CAF50;
}

.switch-thumb {
  display: block;
  width: 20px;
  height: 20px;
  background: white;
  border-radius: 50%;
  position: absolute;
  top: 2px;
  left: 2px;
  transition: transform 0.2s;
}

.switch-thumb[data-state="checked"] {
  transform: translateX(20px);
}
</style>

Settings Panel

<script setup lang="ts">
import { Switch } from '@vuetify/v0'
import { ref } from 'vue'

const settings = ref<string[]>(['dark-mode'])
</script>

<template>
  <div class="settings-panel">
    <h2>Preferences</h2>
    
    <Switch.Group v-model="settings">
      <div class="setting-row">
        <div>
          <h3>Dark Mode</h3>
          <p>Use dark theme across the app</p>
        </div>
        <Switch.Root value="dark-mode">
          <Switch.Track>
            <Switch.Thumb />
          </Switch.Track>
        </Switch.Root>
      </div>

      <div class="setting-row">
        <div>
          <h3>Notifications</h3>
          <p>Receive push notifications</p>
        </div>
        <Switch.Root value="notifications">
          <Switch.Track>
            <Switch.Thumb />
          </Switch.Track>
        </Switch.Root>
      </div>

      <div class="setting-row">
        <div>
          <h3>Auto-save</h3>
          <p>Automatically save your work</p>
        </div>
        <Switch.Root value="auto-save">
          <Switch.Track>
            <Switch.Thumb />
          </Switch.Track>
        </Switch.Root>
      </div>
    </Switch.Group>
  </div>
</template>

Renderless Mode

<script setup lang="ts">
import { Switch } from '@vuetify/v0'
import { ref } from 'vue'

const enabled = ref(false)
</script>

<template>
  <Switch.Root v-model="enabled" renderless v-slot="{ attrs, isChecked, toggle }">
    <button 
      v-bind="attrs"
      @click="toggle"
      class="custom-switch"
      :class="{ active: isChecked }"
    >
      <Switch.Track renderless v-slot="{ isChecked }">
        <span class="track" :data-checked="isChecked">
          <Switch.Thumb renderless v-slot="{ isChecked }">
            <span class="thumb" :data-checked="isChecked" />
          </Switch.Thumb>
        </span>
      </Switch.Track>
    </button>
  </Switch.Root>
</template>

Accessibility

  • Uses role="switch" with aria-checked for proper screen reader support
  • Supports keyboard navigation (Space to toggle)
  • Manages tabindex for proper focus management
  • Provides aria-disabled when disabled
  • Group uses role="group" with optional aria-label
  • Announces current state (“on” or “off”) to screen readers

Keyboard Support

  • Space: Toggle the switch
  • Enter: Toggle the switch (when rendered as button)

Switch vs Checkbox

While both components support similar functionality, they have different semantic meanings:
AspectSwitchCheckbox
SemanticOn/off actionSelection state
ARIA roleswitchcheckbox
Typical useSettings, featuresForm selections, lists
FeedbackImmediate effectDeferred until submission
VisualTrack + thumbBox + checkmark
Use Switch when:
  • The change takes immediate effect (like toggling dark mode)
  • Controlling a single feature or setting
  • The action is clearly “on” or “off”
Use Checkbox when:
  • Selecting items from a list
  • Form submission is involved
  • Multiple independent selections

Type Safety

All components are fully typed with TypeScript generics:
import type { 
  SwitchRootProps,
  SwitchGroupProps,
  SwitchTrackProps,
  SwitchThumbProps,
  SwitchSelectAllProps,
  SwitchState // 'checked' | 'unchecked' | 'indeterminate'
} from '@vuetify/v0'

Build docs developers (and LLMs) love