Skip to main content
Vuetify0 is now a release candidate!
Vuetify0 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

Radio

A radio button for single-selection groups with roving focus and shared v-model state.

Usage

Selected: none

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

  const selected = ref<string>()

  const options = [
    { value: 'small', label: 'Small' },
    { value: 'medium', label: 'Medium' },
    { value: 'large', label: 'Large' },
  ]
</script>

<template>
  <Radio.Group v-model="selected" class="flex flex-col gap-2">
    <label
      v-for="option in options"
      :key="option.value"
      class="inline-flex items-center gap-2"
    >
      <Radio.Root
        class="size-5 border rounded-full inline-flex items-center justify-center border-divider data-[state=checked]:border-primary"
        :value="option.value"
      >
        <Radio.Indicator class="size-2.5 rounded-full bg-primary" />
      </Radio.Root>

      <span>{{ option.label }}</span>
    </label>
  </Radio.Group>

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

Anatomy

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

<template>
  <Radio.Group>
    <Radio.Root>
      <Radio.Indicator />
      <Radio.HiddenInput />
    </Radio.Root>
  </Radio.Group>
</template>

Examples

Checkout Shipping Selector

A checkout step where the customer picks exactly one shipping method. Each option is a full-width selectable card carrying a label, an ETA, and a price, and the chosen method drives a live order summary — selected method, shipping cost, and a computed total layered on top of a fixed cart subtotal. Submitting the form turns the selection into a confirmed order; resetting returns to the default method.

The single-select behavior comes straight from Radio.Group: its v-model holds one value at a time, and picking a new card replaces the previous selection. The group is initialized with standard preselected and marked mandatory so the customer can switch methods but never clear the choice back to “none” — Radio.Root exposes select, not toggle, so clicking the active card is a no-op rather than a deselect. Form participation is handled entirely by the name prop on Radio.Group: it cascades to every Radio.Root, which auto-renders a hidden native input under the shared name, so the selected value posts with the form without any manual Radio.HiddenInput placement. The cards style themselves with has-[:checked]:border-primary against that auto-rendered input — no JavaScript class toggling.

Reach for this pattern whenever a small, fixed set of mutually exclusive options must always resolve to one answer: shipping tiers, payment methods, subscription cadences. Keep the option data and derived totals in the use* composable, the radio markup in a reusable component, and the wiring plus order summary in the entry. If you instead want the first option auto-selected on mount with no initial v-model value, set mandatory="force" on the group rather than preselecting a value. For the underlying single-selection primitive, see createSingle; for the multi-select counterpart, see Checkbox.

FileRole
useShipping.tsComposable — shipping methods, selected method state, derived shipping/total, submit/reset
ShippingOptions.vueReusable component — renders the Form with a mandatory, named Radio.Group of selectable cards
shipping-options.vueEntry — wires the composable to the form and renders the live order summary + confirmation panel
Subtotal
$64
Shipping (Standard)
Free
Total
$64

Recipes

Form Integration

Pass the name prop on Radio.Group and every Radio.Root inside it renders a hidden native radio input automatically — the group’s name cascades to each Radio.Root unless the child sets its own. No Radio.HiddenInput placement is required:

vue
<template>
  <Radio.Group v-model="selected" name="size">
    <Radio.Root value="small">
      <Radio.Indicator />
      Small
    </Radio.Root>

    <Radio.Root value="large">
      <Radio.Indicator />
      Large
    </Radio.Root>
  </Radio.Group>
</template>

Radio.HiddenInput is exported as an internal building block for custom layouts, but auto-rendering via name is the only supported form integration path — placing Radio.HiddenInput as a child of a Radio.Root that already resolves a name (from itself or the parent Radio.Group) will produce two hidden inputs.

Styling with Data Attributes

Radio.Root exposes data attributes for CSS styling without conditional classes:

AttributeValuesNotes
data-statechecked, uncheckedReflects current visual state
data-disabledtruePresent only when disabled
vue
<template>
  <Radio.Root class="size-5 rounded-full border data-[state=checked]:border-primary data-[disabled]:opacity-50">
    <Radio.Indicator class="size-2.5 rounded-full bg-primary" />
  </Radio.Root>
</template>

Accessibility

The Radio components handle all ARIA attributes automatically:

  • role="radiogroup" on the Group

  • role="radio" on each Root

  • aria-checked reflects checked state

  • aria-disabled when radio is disabled

  • aria-required for form validation (set on Group)

  • aria-label from the label prop

  • Roving tabindex — only the selected radio (or first non-disabled if none selected) is tabbable

  • Space key selects the focused radio

  • Arrow keys navigate between radios

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

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

Arrow keys provide circular navigation within a radio group:

KeyAction
SpaceSelect focused radio
ArrowUp / ArrowLeftMove to previous radio
ArrowDown / ArrowRightMove to next radio

Navigation automatically skips disabled items and wraps around.

FAQ

Discord
Need help? Join our community for support and discussions ↗

API Reference

The following API details are for all variations of the Radio component.
Was this page helpful?

Ctrl+/