Radio Group
Accessible, unstyled native radio group with controlled and uncontrolled state.
| Package | @vizejs/ui/radio-group |
| Maturity | stable |
| Own the source | vize lib pull radio-group |
| Requires | context, controllable-state, id |
| Aliases | radio group, choice group, single choice |
| Covers | HTML radio, WAI-ARIA Radio Group, React Aria RadioGroup, Reka UI Radio Group |
Minimal setup
Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).
vp install @vizejs/ui
Add the public import below to <script setup lang="ts">. Components provide behavior without styles; the preview uses optional Paper styles.
import { RadioGroup, RadioGroupItem } from "@vizejs/ui/radio-group";
Or copy the source into your project with vize lib pull radio-group (see Source Distribution).
Try it
This is the basic example maintained with the component source, using the public package imports. The preview adds the optional Paper styles.
Open the interactive example · Styling these components
Copy the example
<!-- Controlled radio group bound with v-model, labelled by visible text, with one disabled option. -->
<script setup lang="ts">
import { ref, useId } from "vue";
import { RadioGroup, RadioGroupItem } from "@vizejs/ui/radio-group";
const options = [
{ value: "standard", label: "Standard (5–7 days)", disabled: false },
{ value: "express", label: "Express (2 days)", disabled: false },
{ value: "overnight", label: "Overnight (unavailable)", disabled: true },
];
const shipping = ref<string | null>("standard");
const baseId = useId();
</script>
<template>
<div>
<span :id="`${baseId}-label`">Shipping speed</span>
<RadioGroup v-model="shipping" name="shipping" :aria-labelledby="`${baseId}-label`">
<div v-for="option in options" :key="option.value">
<RadioGroupItem
:id="`${baseId}-${option.value}`"
:value="option.value"
:disabled="option.disabled"
/>
<label :for="`${baseId}-${option.value}`">{{ option.label }}</label>
</div>
</RadioGroup>
<output>{{ shipping ?? "none" }}</output>
</div>
</template>
API
RadioGroup
Source: src/families/selection/radio-group/radio-group.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
id |
string | null |
undefined |
Consumer-owned radio group id. null and undefined select a deterministic fallback. |
name |
string |
undefined |
Native radio name shared by every item for form submission and browser grouping. |
modelValue |
RadioGroupValue |
undefined |
Controlled selected value. undefined selects uncontrolled behavior; null clears selection. |
defaultValue |
RadioGroupValue |
null |
Initial value for uncontrolled use and the value restored by form reset. |
disabled |
boolean |
false |
Disable every radio item and native form submission. |
required |
boolean |
false |
Mark the native radio set as required for constraint validation. |
orientation |
RadioGroupOrientation |
"vertical" |
Directional layout hint exposed for consumer-owned styles. |
ariaLabel |
string |
undefined |
Accessible name when no visible label or aria-labelledby supplies one. |
ariaLabelledby |
string |
undefined |
Space-separated ids that label the radio group. |
ariaDescribedby |
string |
undefined |
Space-separated ids that describe the radio group. |
ariaErrormessage |
string |
undefined |
Id of the validation error message used while invalid. |
ariaInvalid |
RadioGroupAriaInvalid |
false |
Invalid state announced to assistive technology. |
Events
| Event | Payload | Description |
|---|---|---|
update:modelValue |
[value: RadioGroupValue] |
Fired when the selected value requests a new controlled value. |
change |
[value: string, previous: RadioGroupValue, nativeEvent: Event] |
Fired after user selection requests a distinct radio value. |
Slots
| Slot | Slot props | Description |
|---|---|---|
default |
RadioGroupSlotState |
Compound RadioGroup items. Receives current value, validity, and availability state. |
Exposed
| Member | Type | Description |
|---|---|---|
value |
RadioGroupValue |
Current selected value, or null when no item is selected. |
disabled |
boolean |
Whether every item is disabled by the group. |
required |
boolean |
Whether the group participates in native required validation. |
invalid |
boolean |
Whether the group is currently marked invalid. |
orientation |
RadioGroupOrientation |
Directional layout hint for consumer-owned styling and keyboard help. |
state |
RadioGroupState |
Stable state token for styling and tests. |
id |
string |
Root-owned id for the radio group. |
focus |
(options?: FocusOptions) => void |
Move focus to the checked item, or to the first enabled item. |
setValue |
(value: RadioGroupValue) => boolean |
Request a selected value update and report whether it differs. |
reset |
() => boolean |
Restore the current default value and report whether it changed. |
element |
Readonly<ShallowRef<HTMLElement | null>> |
Template ref to the rendered root element. |
RadioGroupItem
Source: src/families/selection/radio-group/radio-group-item.vue
Props
| Prop | Type | Default | Description |
|---|---|---|---|
id |
string | null |
undefined |
Consumer-owned radio id. null and undefined select a deterministic fallback. |
value |
string |
"on" |
Native radio value submitted when this item is selected. |
disabled |
boolean |
false |
Disable this item while preserving the rest of the group. |
ariaLabel |
string |
undefined |
Accessible name when no associated label supplies one. |
ariaLabelledby |
string |
undefined |
Space-separated ids that label this radio. |
ariaDescribedby |
string |
undefined |
Space-separated ids that describe this radio. |
Exposed
| Member | Type | Description |
|---|---|---|
element |
HTMLInputElement | null |
Rendered native radio input. |
value |
string |
Item value submitted when this radio is selected. |
checked |
boolean |
Whether this item is currently selected. |
disabled |
boolean |
Whether this item is disabled by itself or the group. |
focus |
(options?: FocusOptions) => void |
Move focus to the native radio input. |
Behavior
Normative state × input → outcome table for radio-group.vue and
radio-group-item.vue (@vizejs/ui/radio-group). Every row is proven by the
named mounted-DOM test in src/families/selection/radio-group/radio-group.test.ts; a row without a passing test
is a contract violation.
| # | State | Input | Outcome | Proven by |
|---|---|---|---|---|
| R1 | named, selected | render | native radiogroup root with deterministic id, ARIA field state, orientation data/ARIA, and named native radio items with the selected item checked |
renders native radio group semantics with form and accessibility attributes |
| R2 | uncontrolled | pointer selection | selected value, checked item, native form value, root/item data state, and update:modelValue before change all follow the selected radio |
uncontrolled radio group selects one item and submits its value |
| R3 | controlled | pointer selection | emits the selection request and change; rendered checked state reverts to modelValue until the parent accepts the update |
controlled value wins until the parent accepts the request |
| R4 | uncontrolled, seeded | form reset | defaultValue seeds the initial selection and form reset restores it without request-global state |
defaultValue seeds state and native form reset restores it |
| R5 | focusable | Space / exposed | native radio keyboard activation selects an item; focus() targets the checked item and item refs focus the native input |
keyboard activation and exposed focus follow native radio behavior |
| R6 | disabled | render / Tab | group-disabled items use native disabled, submit no value, emit no changes, and leave sequential focus |
disabled groups and disabled items keep native availability semantics |
| R7 | missing provider | item render | RadioGroupItem outside a matching RadioGroup throws the stable context diagnostic |
items require a matching group provider |
The subpath remains tree-shakable and retains no packaged CSS; those package
contracts are pinned by distribution.test.ts, check:size, and
check:tree-shaking.