RadioGroup
$ npx shadcn@latest add https://lacodda.github.io/dowel/r/radio-group.jsonReach for this when the options are worth the space. A handful of short choices read faster laid out than hidden behind a trigger, and each one becomes a target rather than a step. Past about five, or when the labels are long, a Select is the honest choice — this is not a Select with more pixels.
<RadioGroup aria-label="Ripeness" defaultValue="ripe"> <Radio value="green">Green</Radio> <Radio value="ripe">Ripe</Radio> <Radio value="soft">Soft</Radio></RadioGroup>The group is the control. That is what the arrow keys move within, what a
screen reader announces as one thing with a position in it, and why the group
takes the value rather than each button. Radio outside a group is a checkbox
that cannot be unchecked, which is why it is only useful inside one.
One tab stop, not three. Tab reaches the group and the arrows move the choice — the behaviour a native radio group has and a row of styled buttons does not.
orientation="horizontal" for two or three short options. A column is the
default because it stays readable as labels grow.
Name the group. It is the question the options answer, and without it a reader hears three values and no subject:
<RadioGroup aria-label="Ripeness"> {/* or */}<Field label="Ripeness"><RadioGroup>... {/* named by the field */}Props
Read from the types in ui/radio-group.tsx, the file a product copies - so the description of each prop is also what an editor shows over it there.
RadioGroup
| Prop | Type | Default | |
|---|---|---|---|
childrenrequired | ReactNode | ||
value | string | The value of the selected radio. Controlled; pair it with onValueChange. | |
defaultValue | string | The value selected at first, when the group is uncontrolled. | |
onValueChange | (value: string) => void | Called with the value of the radio the reader chose. | |
disabled | boolean | Disables every radio in the group. | |
readOnly | boolean | Shows the selection but does not let it change. | |
required | boolean | Marks the group as requiring a choice for form validation. | |
name | string | The field name submitted with a form. | |
orientation | horizontal | vertical | null | Lay the options out in a row. For two or three short ones; a column is the default because it stays readable as labels grow. | |
aria-label | string | The accessible name of the group, when no visible label names it. | |
className | string |
Radio
| Prop | Type | Default | |
|---|---|---|---|
valuerequired | string | What the group's value becomes when this radio is chosen. | |
children | ReactNode | The words next to the dot. | |
disabled | boolean | Disables this radio alone; the label dims with it. | |
className | string |
Accessibility
The examples on the stand, through axe 4.14.0 in a browser, in both themes, against WCAG 2.2 A and AA and axe's best practices: 18 rules applied and passed, and none failed. The accessibility report has every item.
Left to a person, where axe could not decide: aria-toggle-field-name, duplicate-id-aria.
History
Each release that changed ui/radio-group.tsx - the file a product copies - newest first.