Examples
One choice
A named group makes one mutually exclusive option easy to understand.
Choose one plan for this workspace.
RadioGroup with a label and description
"use client";
import { Field, FieldDescription, FieldLabel } from "@phuctech/ui/components/field";
import { RadioGroup, RadioGroupItem } from "@phuctech/ui/components/radio-group";
const plans = ["Starter", "Growth", "Scale"] as const;
export function Example() {
return (
<Field>
<FieldLabel>Plan</FieldLabel>
<FieldDescription>Choose one plan for this workspace.</FieldDescription>
<RadioGroup name="plan" defaultValue="Growth" aria-label="Plan">
{plans.map((plan) => (
<label key={plan} className="flex items-center gap-2 text-sm">
<RadioGroupItem value={plan} />
{plan}
</label>
))}
</RadioGroup>
</Field>
);
}
Density
Use sm, md, or lg so the radio matches the surrounding form rhythm.
RadioGroup small, default, and large sizes
"use client";
import { RadioGroup, RadioGroupItem } from "@phuctech/ui/components/radio-group";
export function Example() {
return (
<RadioGroup defaultValue="md" aria-label="Density" className="gap-3">
<label className="flex items-center gap-2 text-sm"><RadioGroupItem size="sm" value="sm" />Small</label>
<label className="flex items-center gap-2 text-sm"><RadioGroupItem size="md" value="md" />Default</label>
<label className="flex items-center gap-2 text-sm"><RadioGroupItem size="lg" value="lg" />Large</label>
</RadioGroup>
);
}
Vertical and horizontal
Choose the layout that matches the number and length of options.
Vertical and horizontal RadioGroup layouts
"use client";
import { RadioGroup, RadioGroupItem } from "@phuctech/ui/components/radio-group";
const options = ["Daily", "Weekly", "Monthly"] as const;
export function Example() {
return (
<div className="grid gap-5">
<RadioGroup defaultValue="Daily" aria-label="Vertical frequency">
{options.map((option) => <label key={option} className="flex items-center gap-2 text-sm"><RadioGroupItem value={option} />{option}</label>)}
</RadioGroup>
<RadioGroup defaultValue="Weekly" aria-label="Horizontal frequency" className="flex flex-wrap gap-4">
{options.map((option) => <label key={option} className="flex items-center gap-2 text-sm"><RadioGroupItem value={option} />{option}</label>)}
</RadioGroup>
</div>
);
}
Controlled value
Feature state owns the selected value when it coordinates another view.
Controlled RadioGroup preview
"use client";
import { RadioGroup, RadioGroupItem } from "@phuctech/ui/components/radio-group";
export function Example() {
return (
<RadioGroup value="Growth" aria-label="Workspace mode">
<label className="flex items-center gap-2 text-sm"><RadioGroupItem value="balanced" />Balanced</label>
<label className="flex items-center gap-2 text-sm"><RadioGroupItem value="focused" />Focused</label>
</RadioGroup>
);
}Disabled and read-only
Unavailable options stay visible while the group keeps its selected value clear.
Disabled option and read-only RadioGroup
"use client";
import { RadioGroup, RadioGroupItem } from "@phuctech/ui/components/radio-group";
export function Example() {
return (
<div className="grid gap-4 text-sm">
<RadioGroup defaultValue="team" aria-label="Access level">
<label className="flex items-center gap-2"><RadioGroupItem value="team" />Team</label>
<label className="flex items-center gap-2"><RadioGroupItem value="private" disabled />Private (unavailable)</label>
</RadioGroup>
<RadioGroup defaultValue="inherited" readOnly aria-label="Inherited access">
<label className="flex items-center gap-2"><RadioGroupItem value="inherited" />Inherited from organisation</label>
</RadioGroup>
</div>
);
}
Native form value
A group submits one value through the native form contract.
RadioGroup with native form semantics
"use client";
import { Button } from "@phuctech/ui/components/button";
import { RadioGroup, RadioGroupItem } from "@phuctech/ui/components/radio-group";
export function Example() {
return (
<form className="grid gap-3" onSubmit={(event) => event.preventDefault()}>
<RadioGroup name="visibility" defaultValue="team" required aria-label="Visibility">
<label className="flex items-center gap-2 text-sm"><RadioGroupItem value="team" />Team</label>
<label className="flex items-center gap-2 text-sm"><RadioGroupItem value="private" />Private</label>
</RadioGroup>
<Button type="submit" size="sm">Save visibility</Button>
</form>
);
}
When to use
Use RadioGroup for a visible set of two or more alternatives where only one value is valid. Use Select when the list is long or space is limited, and Checkbox when choices are independent.
Usage
"use client";
import { Field, FieldDescription, FieldLabel } from "@phuctech/ui/components/field";
import { RadioGroup, RadioGroupItem } from "@phuctech/ui/components/radio-group";
const plans = ["Starter", "Growth", "Scale"] as const;
export function Example() {
return (
<Field>
<FieldLabel>Plan</FieldLabel>
<FieldDescription>Choose one plan for this workspace.</FieldDescription>
<RadioGroup name="plan" defaultValue="Growth" aria-label="Plan">
{plans.map((plan) => (
<label key={plan} className="flex items-center gap-2 text-sm">
<RadioGroupItem value={plan} />
{plan}
</label>
))}
</RadioGroup>
</Field>
);
}
Give the group an accessible name with FieldLabel or aria-label, then give each item a visible label. The group owns the value; the feature owns what changing it means.
Behavior
- Arrow keys move between radio items while Tab enters and leaves the group once.
valueandonValueChangeprovide controlled state;defaultValueis the local alternative.readOnlykeeps the selected value visible while preventing a new choice.
API
Tokens
Accessibility
- The group needs a name and each item needs a readable label.
- Test Arrow keys, Home/End where supported, and Tab order with a keyboard.
- Do not use a radio group for actions that take effect independently.