One choice from a set: native radios sharing a name, with arrow-key movement.
Example
import { RadioGroup, RadioGroupItem } from "@elements/ui/radio-group";
import { Label } from "@elements/ui/label";
<RadioGroup value={form.density} attrs={{ "aria-label": "Density" }}>
<Label><RadioGroupItem value="default" /> Default</Label>
<Label><RadioGroupItem value="compact" /> Compact</Label>
</RadioGroup>
<RadioGroup value={form.plan} options={PLANS} orientation="horizontal" />
Parts
| Part |
What it is |
.radio-group |
The role="radiogroup" container. |
.radio-group-item |
An item wrapper: the radio and the dot stacked. |
.radio-group-item-input |
The native <input type="radio">. |
.radio-group-indicator |
The dot; scales in, so nothing resizes. |
Attributes
RadioGroup
| Attribute |
Type |
Default |
Description |
value |
string (two-way) |
"" |
The checked item's value. |
options |
{ value: string; label: string; disabled?: boolean }[] |
[] |
Items as data, each in a label. Slot items render after. |
name |
string |
"" |
Form field name shared by the radios. Without one the radios still group, but the form submits nothing for them. |
form |
string |
"" |
The id of a form the radios join when the group sits outside it. |
orientation |
"vertical" | "horizontal" |
"vertical" |
Column or row. |
disabled |
boolean |
false |
Disables every item. |
readOnly |
boolean |
false |
The choice cannot change; radios stay focusable and arrows move focus. |
required |
boolean |
false |
The form will not submit until one is chosen. |
invalid |
boolean |
false |
aria-invalid on the group and the danger look on items given as options. |
loop |
boolean |
true |
Arrows wrap from the last item to the first. |
dir |
"ltr" | "rtl" | "" |
"" |
Reading direction; empty follows the page. |
class |
string |
"" |
Extra classes on the group. |
attrs |
Record<string, string | number | boolean | undefined> |
{} |
aria-label, aria-labelledby, id, data-* for the group. |
onValueChange |
(value: string, details: ChangeDetails) => void |
() => {} |
The user chose an item, or a form reset restored the first value (not called for changes in code). It runs before the change with details second (reason, event, cancel(), isCanceled); details.cancel() keeps the checked radio. |
RadioGroupItem
| Attribute |
Type |
Default |
Description |
value |
string |
(required) |
This item's value. |
disabled |
boolean |
false |
This item cannot be chosen; arrows skip it. |
readOnly |
boolean |
false |
This item cannot be chosen; it stays focusable. |
required |
boolean |
false |
required on this radio, even when the group is not. |
invalid |
boolean |
false |
aria-invalid and the danger look. |
class |
string |
"" |
Extra classes on the item wrapper. |
attrs |
Record<string, string | number | boolean | undefined> |
{} |
id, aria-describedby, aria-label, data-* for the radio. |
Slots
| Slot |
Part |
Content |
default |
RadioGroup |
RadioGroupItem parts, in labels, Fields or choice cards. |
Keyboard
| Keys |
Action |
Tab |
Moves into the group on the checked radio, or the first when none is. |
Space |
Checks the focused radio. |
Enter |
Does nothing: it neither checks the radio nor submits the form. |
ArrowDown / ArrowRight |
Moves to the next enabled radio and checks it (ArrowLeft in right to left). |
ArrowUp / ArrowLeft |
Moves to the previous enabled radio and checks it. |
Data attributes
| Attribute |
Values |
On |
Description |
data-orientation |
"vertical" | "horizontal" |
group |
|
data-disabled |
present while disabled |
group, item, indicator |
|
data-state |
"checked" | "unchecked" |
item, radio, indicator |
|
data-checked, data-unchecked |
present for the current state |
item, indicator |
Presence flags that mirror data-state. |
data-readonly |
present while read only |
group, item, radio |
|
data-required |
present while the radio is required |
item |
|
Accessibility
- role="radiogroup" around native radios: each radio's checked state and position in the set are the browser's.
- Name the group with aria-label or aria-labelledby in attrs, or a FieldSet legend; name each radio with a Label.
- One tab stop: Tab lands on the checked radio. Arrows move and check, skipping disabled items.
- required puts required on the radios, so the browser blocks submit and reports it.
See also