Skip to main content
You are viewing Pre-Alpha documentation.
Vuetify0 Logo
Theme
Mode
Accessibility
Vuetify
Vuetify One

Sign in to Vuetify One

Access premium tools across the Vuetify ecosystem — Bin, Play, Studio, and more.

Not a subscriber? See what's included

Switch

A headless switch component with dual-mode support: standalone boolean binding or group multi-selection with tri-state.


IntermediateMar 3, 2026

Usage

The Switch component supports two modes:

  • Standalone mode: Use v-model on Switch.Root for simple boolean state

  • Group mode: Wrap in Switch.Group for multi-selection with array v-model

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

  const enabled = shallowRef(false)
</script>

<template>
  <label class="inline-flex items-center gap-3 cursor-pointer">
    <Switch.Root
      v-model="enabled"
      class="inline-flex items-center border-none bg-transparent p-0 outline-none"
    >
      <Switch.Track
        class="relative inline-flex items-center w-11 h-6 rounded-full bg-gray-300 transition-colors data-[state=checked]:bg-primary"
      >
        <Switch.Thumb
          class="![visibility:visible] block size-4 rounded-full bg-white shadow-sm transition-transform translate-x-1 data-[state=checked]:translate-x-6"
        />
      </Switch.Track>
    </Switch.Root>
    <span>Enable notifications</span>
  </label>
</template>

Anatomy

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

<template>
  <!-- Standalone -->
  <Switch.Root>
    <Switch.Track>
      <Switch.Thumb />
    </Switch.Track>
  </Switch.Root>

  <!-- Group -->
  <Switch.Group>
    <Switch.Root>
      <Switch.Track>
        <Switch.Thumb />
      </Switch.Track>
    </Switch.Root>

    <Switch.Root>
      <Switch.Track>
        <Switch.Thumb />
      </Switch.Track>
    </Switch.Root>
  </Switch.Group>

  <!-- Group with Select All -->
  <Switch.Group>
    <Switch.SelectAll>
      <Switch.Track>
        <Switch.Thumb />
      </Switch.Track>
    </Switch.SelectAll>

    <Switch.Root>
      <Switch.Track>
        <Switch.Thumb />
      </Switch.Track>
    </Switch.Root>
  </Switch.Group>

  <!-- With form submission -->
  <Switch.Root>
    <Switch.Track>
      <Switch.Thumb />
    </Switch.Track>

    <Switch.HiddenInput />
  </Switch.Root>
</template>

Accessibility

The Switch.Root component renders as a button and handles all ARIA attributes automatically:

  • role="switch" for proper semantics

  • aria-checked reflects state (true, false, or "mixed")

  • aria-disabled when switch is disabled

  • aria-label from the label prop

  • tabindex="0" for keyboard focus (removed when disabled)

  • Space key toggles the switch

For custom implementations, use renderless mode and bind the attrs slot prop to your element:

vue
<template>
  <Switch.Root v-slot="{ attrs }" renderless>
    <div v-bind="attrs">
      <!-- Custom switch visual -->
    </div>
  </Switch.Root>
</template>

Recipes

Group Mode

Wrap switches in Switch.Group for multi-selection with array-based v-model:

Selected: none

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

  const selected = ref<string[]>([])

  const options = [
    { value: 'wifi', label: 'Wi-Fi' },
    { value: 'bluetooth', label: 'Bluetooth' },
    { value: 'location', label: 'Location services' },
  ]
</script>

<template>
  <Switch.Group v-model="selected" class="flex flex-col gap-3" label="Connectivity">
    <label
      v-for="option in options"
      :key="option.value"
      class="inline-flex items-center justify-between gap-4 cursor-pointer"
    >
      <span>{{ option.label }}</span>
      <Switch.Root
        class="inline-flex items-center border-none bg-transparent p-0 outline-none"
        :value="option.value"
      >
        <Switch.Track
          class="relative inline-flex items-center w-11 h-6 rounded-full bg-gray-300 transition-colors data-[state=checked]:bg-primary"
        >
          <Switch.Thumb
            class="![visibility:visible] block size-4 rounded-full bg-white shadow-sm transition-transform translate-x-1 data-[state=checked]:translate-x-6"
          />
        </Switch.Track>
      </Switch.Root>
    </label>
  </Switch.Group>

  <p class="mt-4 text-sm text-on-surface-variant">
    Selected: {{ selected.join(', ') || 'none' }}
  </p>
</template>

Form Integration

When the name prop is provided on Switch.Root, a hidden native checkbox is automatically rendered for form submission:

vue
<template>
  <!-- Auto-renders hidden input for form submission -->
  <Switch.Root name="notifications" value="on">
    <Switch.Track>
      <Switch.Thumb />
    </Switch.Track>
  </Switch.Root>
</template>

For custom form integration, use Switch.HiddenInput explicitly:

vue
<template>
  <Switch.Root>
    <Switch.Track>
      <Switch.Thumb />
    </Switch.Track>

    <Switch.HiddenInput name="custom" value="override" />
  </Switch.Root>
</template>

Indeterminate State

Use Switch.SelectAll within a group for “select all” patterns. It automatically reflects the group’s aggregate state and toggles all items on click:

Selected: none

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

  const selected = ref<string[]>([])

  const options = [
    { value: 'camera', label: 'Camera' },
    { value: 'microphone', label: 'Microphone' },
    { value: 'notifications', label: 'Notifications' },
  ]
</script>

<template>
  <Switch.Group v-model="selected" label="Permissions">
    <div class="flex flex-col gap-3">
      <label class="inline-flex items-center justify-between gap-4 cursor-pointer font-medium">
        <span>All permissions</span>
        <Switch.SelectAll
          class="inline-flex items-center border-none bg-transparent p-0 outline-none"
          label="Toggle all"
        >
          <Switch.Track
            class="relative inline-flex items-center w-11 h-6 rounded-full bg-gray-300 transition-colors data-[state=checked]:bg-primary data-[state=indeterminate]:bg-gray-400"
          >
            <Switch.Thumb
              class="![visibility:visible] block size-4 rounded-full bg-white shadow-sm transition-transform translate-x-1 data-[state=checked]:translate-x-6 data-[state=indeterminate]:translate-x-3.5"
            />
          </Switch.Track>
        </Switch.SelectAll>
      </label>

      <div class="ml-6 flex flex-col gap-3">
        <label
          v-for="option in options"
          :key="option.value"
          class="inline-flex items-center justify-between gap-4 cursor-pointer"
        >
          <span>{{ option.label }}</span>
          <Switch.Root
            class="inline-flex items-center border-none bg-transparent p-0 outline-none"
            :value="option.value"
          >
            <Switch.Track
              class="relative inline-flex items-center w-11 h-6 rounded-full bg-gray-300 transition-colors data-[state=checked]:bg-primary"
            >
              <Switch.Thumb
                class="![visibility:visible] block size-4 rounded-full bg-white shadow-sm transition-transform translate-x-1 data-[state=checked]:translate-x-6"
              />
            </Switch.Track>
          </Switch.Root>
        </label>
      </div>
    </div>
  </Switch.Group>

  <p class="mt-4 text-sm text-on-surface-variant">
    Selected: {{ selected.join(', ') || 'none' }}
  </p>
</template>

The SelectAll component:

  • Binds to the group’s isAllSelected and isMixed state

  • Calls toggleAll on click

  • Does NOT register as a group item

  • Sets aria-checked="mixed" and data-state="indeterminate" when partially selected

Styling with Data Attributes

Switch components expose data-state attributes for CSS styling:

AttributeValuesComponents
data-statechecked, unchecked, indeterminateRoot, Track, Thumb
data-disabledtrueRoot
vue
<template>
  <Switch.Root class="...">
    <Switch.Track class="bg-gray-300 data-[state=checked]:bg-primary">
      <Switch.Thumb class="translate-x-0.5 data-[state=checked]:translate-x-5.5" />
    </Switch.Track>
  </Switch.Root>
</template>

API Reference

The following API details are for all variations of the Switch component.

Switch.Root

Props

id

ID

Unique identifier (auto-generated if not provided)

Default: useId()

label

string

Optional display label (passed through to slot)

value

V

Value associated with this switch (used in group mode and form submission)

name

string

Form field name - triggers auto hidden input when provided

form

string

Associate with form by ID

disabled

MaybeRef<boolean>

Disables this switch

Default: false

indeterminate

MaybeRef<boolean>

Sets the indeterminate state

Default: false

namespace

string

Namespace for context provision to children (Track, Thumb, HiddenInput)

Default: "v0:switch:root"

groupNamespace

string

Namespace for connecting to parent Switch.Group

Default: "v0:switch:group"

ariaLabelledby

string

ID of element that labels this switch

ariaDescribedby

string

ID of element that describes this switch

ariaInvalid

boolean

Whether the switch has an invalid value

modelValue

boolean

Events

update:model-value

[value: boolean]

Slots

default

SwitchRootSlotProps<V>

Switch.Group

Props

namespace

string

Namespace for context provision to children

Default: "v0:switch:group"

disabled

boolean

Disables all switches in the group

Default: false

enroll

boolean

Auto-select items on registration

Default: false

mandatory

boolean | "force"

Require at least one switch to be on. `'force'` prevents deselecting the last item

Default: false

label

string

Accessible group label

ariaLabelledby

string

ID of element that labels this group

ariaDescribedby

string

ID of element that describes this group

modelValue

T | T[]

Events

update:model-value

[value: T | T[]]

Slots

default

SwitchGroupSlotProps

Switch.HiddenInput

Props

value

string

Form field value (overrides Root's value, defaults to 'on')

name

string

Form field name (overrides Root's name)

namespace

string

Namespace for connecting to parent Switch.Root

Default: "v0:switch:root"

form

string

Associate with form by ID (overrides Root's form)

Switch.SelectAll

Props

label

string

Accessible label for the select-all toggle

disabled

boolean

Disables this toggle

namespace

string

Namespace for context provision to children (Track, Thumb)

groupNamespace

string

Namespace for connecting to parent Switch.Group

ariaLabelledby

string

ID of element that labels this toggle

ariaDescribedby

string

ID of element that describes this toggle

Slots

default

SwitchSelectAllSlotProps

Switch.Thumb

Props

namespace

string

Namespace for connecting to parent Switch.Root

Default: "v0:switch:root"

Slots

default

SwitchThumbSlotProps

Switch.Track

Props

namespace

string

Namespace for connecting to parent Switch.Root

Default: "v0:switch:root"

Slots

default

SwitchTrackSlotProps
Was this page helpful?

© 2016-1970 Vuetify, LLC
Ctrl+/