<script setup>
import { ref } from "vue";
import { Radio } from "@dicehub/phi/components/radio";
const value = ref("email");
</script>
<template>
<Radio.Group
v-model="value"
legend="Notification preference"
>
<Radio.Item label="Email" value="email" />
<Radio.Item label="SMS" value="sms" />
<Radio.Item label="Push notification" value="push" />
</Radio.Group>
</template>Installation
Barrel
import { Radio } from "@dicehub/phi";Granular
import { Radio } from "@dicehub/phi/components/radio";Usage
<script setup>
import { ref } from "vue";
import { Radio } from "@dicehub/phi/components/radio";
const value = ref("a");
</script>
<template>
<Radio.Group v-model="value" legend="Choose an option">
<Radio.Item label="Option A" value="a" />
<Radio.Item label="Option B" value="b" />
</Radio.Group>
</template>Examples
Default (Vertical)
Radio groups display vertically by default. Each radio has a label displayed to its right.
<script setup>
import { ref } from "vue";
import { Radio } from "@dicehub/phi/components/radio";
const value = ref("personal");
</script>
<template>
<Radio.Group v-model="value" legend="Account type">
<Radio.Item label="Personal" value="personal" />
<Radio.Item label="Business" value="business" />
<Radio.Item label="Enterprise" value="enterprise" />
</Radio.Group>
</template>Horizontal
Use orientation="horizontal" for inline layouts. Items wrap when there is not enough space.
<script setup>
import { ref } from "vue";
import { Radio } from "@dicehub/phi/components/radio";
const value = ref("md");
</script>
<template>
<Radio.Group
v-model="value"
legend="Size"
orientation="horizontal"
>
<Radio.Item label="Small" value="sm" />
<Radio.Item label="Medium" value="md" />
<Radio.Item label="Large" value="lg" />
</Radio.Group>
</template>With Description
Add helper text below the radio items using the description prop.
<script setup>
import { ref } from "vue";
import { Radio } from "@dicehub/phi/components/radio";
const value = ref("standard");
</script>
<template>
<Radio.Group
v-model="value"
legend="Shipping method"
description="Choose how you'd like to receive your order"
>
<Radio.Item label="Standard (5-7 days)" value="standard" />
<Radio.Item label="Express (2-3 days)" value="express" />
<Radio.Item label="Overnight" value="overnight" />
</Radio.Group>
</template>Control Position
Use controlPosition="end" to place labels before radio buttons.
<script setup>
import { Radio } from "@dicehub/phi/components/radio";
</script>
<template>
<Radio.Group
legend="Preferences"
control-position="end"
default-value="a"
>
<Radio.Item label="Label before radio" value="a" />
<Radio.Item label="Another option" value="b" />
</Radio.Group>
</template>Radio Card
Use appearance="card" to join options inside one card with dividers. Each item can include a description. Selected rows keep their tint when hovered.
<script setup>
import { ref } from "vue";
import { Radio } from "@dicehub/phi/components/radio";
const value = ref("free");
</script>
<template>
<Radio.Group v-model="value" legend="Choose a plan" appearance="card">
<Radio.Item
label="Free"
description="For personal or hobby projects that aren't business-critical."
value="free"
/>
<Radio.Item
label="Pro"
description="For professional websites that aren't business-critical."
value="pro"
/>
<Radio.Item
label="Business"
description="For small businesses operating online."
value="business"
/>
<Radio.Item
label="Contract"
description="For mission-critical applications that are core to your business."
value="contract"
/>
</Radio.Group>
</template>Radio Card (Control on the Left)
Use controlPosition="start" on a card radio group to place the radio control on the left of the label and description.
<script setup>
import { ref } from "vue";
import { Radio } from "@dicehub/phi/components/radio";
const value = ref("free");
</script>
<template>
<Radio.Group
v-model="value"
legend="Choose a plan"
appearance="card"
control-position="start"
>
<Radio.Item
label="Free"
description="For personal or hobby projects that aren't business-critical."
value="free"
/>
<Radio.Item
label="Pro"
description="For professional websites that aren't business-critical."
value="pro"
/>
</Radio.Group>
</template>Rich Label Content
The label slot on Radio.Item accepts rich content, so you can embed icons, badges, or other markup alongside the text.
<script setup>
import { ref } from "vue";
import { Badge } from "@dicehub/phi/components/badge";
import { Radio } from "@dicehub/phi/components/radio";
const value = ref("pro");
</script>
<template>
<Radio.Group v-model="value" legend="Choose a plan" appearance="card">
<Radio.Item
description="For personal or hobby projects."
value="free"
>
<template #label>
<span class="radio-rich-label">Free <Badge variant="neutral">$0</Badge></span>
</template>
</Radio.Item>
<Radio.Item
description="For professional websites."
value="pro"
>
<template #label>
<span class="radio-rich-label">Pro <Badge variant="primary">Popular</Badge></span>
</template>
</Radio.Item>
</Radio.Group>
</template>Radio Card (Horizontal)
Combine appearance="card" with orientation="horizontal" for a joined two-column card with row and column dividers. Below 641px, the options form one column.
<script setup>
import { ref } from "vue";
import { Radio } from "@dicehub/phi/components/radio";
const value = ref("free");
</script>
<template>
<Radio.Group
v-model="value"
legend="Choose a plan"
appearance="card"
orientation="horizontal"
>
<Radio.Item
label="Free"
description="For personal or hobby projects that aren't business-critical."
value="free"
/>
<Radio.Item
label="Pro"
description="For professional websites that aren't business-critical."
value="pro"
/>
<Radio.Item
label="Business"
description="For small businesses operating online."
value="business"
/>
<Radio.Item
label="Contract"
description="For mission-critical applications that are core to your business."
value="contract"
/>
</Radio.Group>
</template>With Error
Show validation errors at the group level using the error prop.
<script setup>
import { Radio } from "@dicehub/phi/components/radio";
</script>
<template>
<div class="radio-demo-grid">
<Radio.Group
legend="Payment method"
error="Please select a payment method to continue"
>
<Radio.Item label="Credit Card" value="card" variant="error" />
<Radio.Item label="PayPal" value="paypal" variant="error" />
</Radio.Group>
<Radio.Group
legend="Payment method"
appearance="card"
error="Please select a payment method to continue"
>
<Radio.Item
label="Credit Card"
description="Pay with Visa, Mastercard, American Express, or Elo."
value="card"
variant="error"
/>
<Radio.Item
label="PayPal"
description="Pay with your PayPal account."
value="paypal"
variant="error"
/>
</Radio.Group>
</div>
</template>Disabled
Use the disabled prop to disable the entire group or individual items.
<script setup>
import { Radio } from "@dicehub/phi/components/radio";
</script>
<template>
<div class="radio-demo-grid">
<Radio.Group legend="Disabled group" disabled default-value="a">
<Radio.Item label="Option A" value="a" />
<Radio.Item label="Option B" value="b" />
</Radio.Group>
<Radio.Group legend="Individual disabled" default-value="available">
<Radio.Item label="Available" value="available" />
<Radio.Item label="Unavailable" value="unavailable" disabled />
</Radio.Group>
<Radio.Group
legend="Disabled card group"
appearance="card"
disabled
default-value="a"
>
<Radio.Item
label="Option A"
description="This option is disabled."
value="a"
/>
<Radio.Item
label="Option B"
description="This option is disabled."
value="b"
/>
</Radio.Group>
<Radio.Group
legend="Individual disabled card"
appearance="card"
default-value="available"
>
<Radio.Item
label="Available"
description="This option can be selected."
value="available"
/>
<Radio.Item
label="Unavailable"
description="This option is not available."
value="unavailable"
disabled
/>
</Radio.Group>
</div>
</template>Visually Hidden Legend
Use Radio.Legend with phi-sr-only to keep context available to screen readers.
<script setup>
import { ref } from "vue";
import { Radio } from "@dicehub/phi/components/radio";
const value = ref("all");
</script>
<template>
<Radio.Group v-model="value" appearance="card" orientation="horizontal">
<Radio.Legend class="phi-sr-only">Paths</Radio.Legend>
<Radio.Item label="Allow all paths" value="all" />
<Radio.Item label="Restrict to specific paths" value="specific" />
</Radio.Group>
</template>Custom Legend Styling
Use Radio.Legend when you need custom typography, colors, or layout.
<script setup>
import { ref } from "vue";
import { Radio } from "@dicehub/phi/components/radio";
const value = ref("email");
</script>
<template>
<Radio.Group v-model="value">
<Radio.Legend style="font-size: 0.8125rem; font-weight: 400; color: var(--phi-subtle);">
Notification preference
</Radio.Legend>
<Radio.Item label="Email" value="email" />
<Radio.Item label="SMS" value="sms" />
<Radio.Item label="Push notification" value="push" />
</Radio.Group>
<Radio.Group v-model="value" appearance="card" orientation="horizontal">
<Radio.Legend style="font-size: 0.8125rem; font-weight: 400; color: var(--phi-subtle);">
Notification preference
</Radio.Legend>
<Radio.Item label="Email" value="email" />
<Radio.Item label="SMS" value="sms" appearance="default" />
<Radio.Item label="Push notification" value="push" />
</Radio.Group>
</template>Typed Values
Radio.Group preserves string, number, and boolean values passed through v-model.
<script setup lang="ts">
import { ref } from "vue";
import { Radio } from "@dicehub/phi/components/radio";
const pageSize = ref(10);
const theme = ref("system");
</script>
<template>
<div class="radio-demo-grid">
<Radio.Group v-model="pageSize" legend="Items per page">
<Radio.Item label="10" :value="10" />
<Radio.Item label="25" :value="25" />
<Radio.Item label="50" :value="50" />
</Radio.Group>
<Radio.Group v-model="theme" legend="Theme">
<Radio.Item label="Light" value="light" />
<Radio.Item label="Dark" value="dark" />
<Radio.Item label="System" value="system" />
</Radio.Group>
</div>
</template>API Reference
Radio.Group
Container for radio buttons with legend, description, and error support.
| Prop / Event | Type | Default | Description |
|---|---|---|---|
legend | string | - | Legend text for the group. For custom styling, omit this prop and pass Radio.Legend as a direct child. Legends render above the items; this prop takes precedence. |
default slot | unknown | - | Child Radio.Item components and optionally a Radio.Legend. |
orientation | "vertical" | "horizontal" | "vertical" | Layout direction. Horizontal card groups use two columns, or one column below 641px. |
appearance | "default" | "card" | "default" | Card groups share one outline with internal dividers. Individual items can override this with appearance; default items retain padding inside a card group, and a card item in a default group keeps its own border. |
description | string | - | Helper text displayed below the group. |
error | string | - | Validation message displayed below the group. |
defaultValue | RadioValue | - | Initial selected value for uncontrolled usage. |
modelValue | RadioValue | - | Controlled selected value for v-model. |
value | RadioValue | - | Controlled selected value alias for modelValue. |
disabled | boolean | - | Disables every item in the group. |
controlPosition | "start" | "end" | - | start places the radio before the label; end places the label before the radio. Defaults to start for default appearance and end for card appearance. |
name | string | - | Native radio group name for form submission. |
class | string | - | Additional CSS classes applied to the fieldset. |
@update:model-value | (value: RadioValue) => void | - | v-model update event. |
@value-change | (value: RadioValue, details: RadioValueChangeDetails) => void | - | Emitted when the selected value changes. The second argument carries the native event. |
Radio.Legend
Composable legend sub-component for Radio.Group.
| Prop / Event | Type | Default | Description |
|---|---|---|---|
default slot | unknown | - | Legend content. |
class | string | - | Additional CSS classes, for example phi-sr-only to visually hide the legend. |
Radio.Item
Individual radio button within Radio.Group.
| Prop / Event | Type | Default | Description |
|---|---|---|---|
variant | "default" | "error" | "default" | Visual variant for validation states. |
appearance | "default" | "card" | - | Overrides the group-level appearance. |
label | string | - | Visible label rendered next to the radio item. |
label slot | unknown | - | Rich Vue content for the item label. |
description | string | - | Description text displayed below the label in card appearance. |
description slot | unknown | - | Rich description content displayed in card appearance. |
value | RadioValue | - | Item value used by Radio.Group. |
class | string | - | Additional CSS classes applied to the label wrapper. |
disabled | boolean | - | Disables this radio item. |
@value-change | (value: RadioValue, details: RadioValueChangeDetails) => void | - | Emitted when this item is selected. |
Accessibility
Semantic HTML
Radio.Group uses semantic fieldset and legend elements for proper grouping and screen reader announcement.
Keyboard Navigation
Arrow keys move between options. Space selects the focused option. Tab moves focus to and from the radio group.
Screen Readers
Each radio is announced with its label and selection state. The group legend provides context for all options.