Skip to main content
You are viewing Pre-Alpha documentation.
Vuetify0 LogoVuetify0 Logo
Theme
Mode
Palettes
Accessibility
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

Toggle

A headless toggle button with dual-mode support: standalone boolean binding or group selection with single and multi-select.


Renders elementIntermediateApr 3, 2026

Usage

The Toggle component supports two modes:

  • Standalone mode: Use v-model on Toggle.Root for simple boolean on/off state

  • Group mode: Wrap in Toggle.Group for single or multi-select with value-based selection

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

  const saved = shallowRef(false)
</script>

<template>
  <div class="flex justify-center">
    <Toggle.Root
      v-model="saved"
      class="inline-flex items-center gap-2 px-4 py-2 rounded-lg border border-divider font-medium data-[state=on]:bg-primary data-[state=on]:text-on-primary data-[state=on]:border-primary"
    >
      <svg
        class="size-5 data-[state=off]:opacity-40"
        :data-state="saved ? 'on' : 'off'"
        stroke="currentColor"
        stroke-width="2"
        viewBox="0 0 24 24"
      >
        <path
          d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"
          :fill="saved ? 'currentColor' : 'none'"
        />
      </svg>

      Bookmark
    </Toggle.Root>
  </div>
</template>

Anatomy

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

<template>
  <!-- Standalone -->
  <Toggle.Root>
    <Toggle.Indicator />
  </Toggle.Root>

  <!-- Group (single select) -->
  <Toggle.Group>
    <Toggle.Root>
      <Toggle.Indicator />
    </Toggle.Root>

    <Toggle.Root>
      <Toggle.Indicator />
    </Toggle.Root>

    <Toggle.Root>
      <Toggle.Indicator />
    </Toggle.Root>
  </Toggle.Group>

  <!-- Group (multi select) -->
  <Toggle.Group multiple>
    <Toggle.Root>
      <Toggle.Indicator />
    </Toggle.Root>

    <Toggle.Root>
      <Toggle.Indicator />
    </Toggle.Root>

    <Toggle.Root>
      <Toggle.Indicator />
    </Toggle.Root>
  </Toggle.Group>
</template>

Examples

Toolbar

Use Toggle.Group with multiple to build a formatting toolbar. Each toggle operates independently — any combination can be active.

The quick brown fox jumps over the lazy dog. This sentence demonstrates how your formatting selections apply in real time.

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

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

<template>
  <div class="rounded-lg border border-divider overflow-hidden">
    <div class="flex items-center gap-1 px-2 py-1.5 border-b border-divider bg-surface-variant/30">
      <Toggle.Group v-model="formatting" class="inline-flex gap-2 rounded overflow-hidden" multiple>
        <Toggle.Root
          class="size-8 flex items-center justify-center py-1 font-bold text-sm text-on-surface-variant rounded data-[state=on]:bg-primary data-[state=on]:text-on-primary"
          value="bold"
        >
          B
        </Toggle.Root>

        <Toggle.Root
          class="size-8 flex items-center justify-center py-1 italic text-sm text-on-surface-variant rounded data-[state=on]:bg-primary data-[state=on]:text-on-primary"
          value="italic"
        >
          I
        </Toggle.Root>

        <Toggle.Root
          class="size-8 flex items-center justify-center py-1 underline text-sm text-on-surface-variant rounded data-[state=on]:bg-primary data-[state=on]:text-on-primary"
          value="underline"
        >
          U
        </Toggle.Root>

        <Toggle.Root
          class="size-8 flex items-center justify-center py-1 line-through text-sm text-on-surface-variant rounded data-[state=on]:bg-primary data-[state=on]:text-on-primary"
          value="strikethrough"
        >
          S
        </Toggle.Root>
      </Toggle.Group>
    </div>

    <p
      class="px-4 py-3 text-sm leading-relaxed"
      :class="{
        'font-bold': formatting.includes('bold'),
        'italic': formatting.includes('italic'),
        'underline': formatting.includes('underline'),
        'line-through': formatting.includes('strikethrough'),
      }"
    >
      The quick brown fox jumps over the lazy dog. This sentence demonstrates how your formatting selections apply in real time.
    </p>
  </div>
</template>

View Switcher

Use Toggle.Group with mandatory for mutually exclusive options like layout switchers. The mandatory prop prevents deselecting all items.

6 folders
Photos
248 items
Documents
53 items
Music
112 items
Videos
37 items
Archives
19 items
Downloads
84 items
<script setup lang="ts">
  import { Toggle } from '@vuetify/v0'
  import { shallowRef } from 'vue'

  const view = shallowRef('grid')

  const items = [
    { title: 'Photos', count: 248 },
    { title: 'Documents', count: 53 },
    { title: 'Music', count: 112 },
    { title: 'Videos', count: 37 },
    { title: 'Archives', count: 19 },
    { title: 'Downloads', count: 84 },
  ]
</script>

<template>
  <div class="flex flex-col gap-6">
    <div class="flex items-center justify-between">
      <span class="text-sm font-medium text-on-surface-variant">{{ items.length }} folders</span>

      <Toggle.Group v-model="view" class="inline-flex rounded-lg border border-divider overflow-hidden" mandatory>
        <Toggle.Root
          class="p-2 transition-colors data-[state=on]:bg-primary data-[state=on]:text-on-primary"
          value="grid"
        >
          <svg
            class="size-5"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            viewBox="0 0 24 24"
          >
            <rect
              height="7"
              rx="1"
              width="7"
              x="3"
              y="3"
            />
            <rect
              height="7"
              rx="1"
              width="7"
              x="14"
              y="3"
            />
            <rect
              height="7"
              rx="1"
              width="7"
              x="3"
              y="14"
            />
            <rect
              height="7"
              rx="1"
              width="7"
              x="14"
              y="14"
            />
          </svg>
        </Toggle.Root>

        <Toggle.Root
          class="p-2 border-l border-divider transition-colors data-[state=on]:bg-primary data-[state=on]:text-on-primary"
          value="list"
        >
          <svg
            class="size-5"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
            viewBox="0 0 24 24"
          >
            <line x1="3" x2="21" y1="6" y2="6" />
            <line x1="3" x2="21" y1="12" y2="12" />
            <line x1="3" x2="21" y1="18" y2="18" />
          </svg>
        </Toggle.Root>
      </Toggle.Group>
    </div>

    <div
      :class="view === 'grid'
        ? 'grid grid-cols-3 gap-4'
        : 'flex flex-col gap-2'"
    >
      <div
        v-for="item in items"
        :key="item.title"
        :class="view === 'grid'
          ? 'flex flex-col items-center gap-2 p-4 rounded-lg border border-divider'
          : 'flex items-center justify-between px-4 py-3 rounded-lg border border-divider'"
      >
        <svg
          class="text-on-surface-variant"
          :class="view === 'grid' ? 'size-8' : 'size-5'"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
          viewBox="0 0 24 24"
        >
          <path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" />
        </svg>

        <div :class="view === 'grid' ? 'text-center' : 'flex-1 ml-3'">
          <div class="text-sm font-medium">{{ item.title }}</div>
          <div class="text-xs text-on-surface-variant">{{ item.count }} items</div>
        </div>
      </div>
    </div>
  </div>
</template>

Accessibility

Toggle renders as a native <button> element with proper ARIA attributes:

AttributeValueDescription
aria-pressedtrue / falseReflects the pressed state
aria-disabledtrue / absentPresent when disabled

Group ARIA

AttributeValueDescription
rolegroupIdentifies the toggle group
aria-orientationhorizontal / verticalLayout direction
aria-disabledtrue / absentPresent when group is disabled

Keyboard

KeyAction
SpaceToggle pressed state
EnterToggle pressed state (native button behavior)

Data Attributes

AttributeValuesDescription
data-stateon / offCSS styling hook for pressed state
data-disabledpresent / absentCSS styling hook for disabled state
data-orientationhorizontal / verticalGroup orientation (on group element)

API Reference

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

Toggle.Root

Props

id

any

Unique identifier (auto-generated if not provided)

Default: useId()

value

V | undefined

Selection key when inside a Toggle.Group

disabled

boolean | undefined

Disables this toggle

Default: false

namespace

string | undefined

Namespace for context provision to children (Indicator)

Default: "v0:toggle:root"

groupNamespace

string | undefined

Namespace for connecting to parent Toggle.Group

Default: "v0:toggle:group"

modelValue

boolean | undefined

Events

update:model-value

[value: boolean]

Slots

default

ToggleRootSlotProps

Toggle.Group

Props

namespace

string | undefined

Namespace for dependency injection

Default: "v0:toggle:group"

disabled

boolean | undefined

Disables the entire toggle group

Default: false

multiple

boolean | undefined

Allow multiple toggles active at once

Default: false

mandatory

boolean | "force" | undefined

Prevent deselecting the last active toggle

Default: false

orientation

ToggleOrientation | undefined

Layout direction for ARIA

Default: "horizontal"

modelValue

T | T[] | undefined

Events

update:model-value

[value: T | T[]]

Slots

default

ToggleGroupSlotProps

Toggle.Indicator

Props

namespace

string | undefined

Namespace for context injection from parent Toggle.Root

Default: "v0:toggle:root"

Slots

default

ToggleIndicatorSlotProps
Was this page helpful?

© 2016-1970 Vuetify, LLC
Ctrl+/