RadioGroup
Preview appears after the next Storybook update.
<RadioGroup />When to use
Use it for
- Two to about six exclusive options that benefit from being compared side by side.
- Options that need a description to choose well.
- horizontal only for two or three short options, like Light, Dark, System.
Not for
- More than about six options, or options users know by name: use Select.
- A single on or off: use Switch or Checkbox.
- Switching views of content: use Tabs.
Do and don't
Every example is rendered with the real component, so it always matches what ships.
Preselect the safest or most common option
A default saves a decision; pick the one most teams want.
A single radio button
One radio cannot be unselected. Use a Checkbox.
Accessibility
- Base UI RadioGroup: role radiogroup named by the legend; one tab stop, arrow keys move and select.
- Each radio is labelled by its text (aria-labelledby) and described by its description.
- The group error is linked with aria-describedby and sets aria-invalid; it shows an icon and text.
- Selected uses the ink fill with a light dot; the unselected edge is color.border.strong (3:1).
Content guidelines
- Legend asks the question; options answer it in parallel form.
- Keep option labels to a few words; move detail to the description.
- Errors say what to do: "Pick a merge strategy before you publish."
From the code Generated from the component, not editable here.
Anatomy and tokens
Read from packages/react/src/components/RadioGroup/RadioGroup.module.css at build time. Every value is a token: open one to see its alias chain in both themes.
- Fieldset container
.fieldset - Legend (group label)
.legend - Fieldset container, disabled
.fieldset:disabled .legend - hiddenLegend
.hiddenLegend - required
.required - description
.description, .error - description
.description - Error message
.error - errorIcon
.errorIcon - Radio list
.group - horizontal
.horizontal - Option row (click target)
.item - Radio circle
.radio - Option row (click target), hover
@media (hover: hover) .item:hover .radio:not([data-disabled], [data-checked]) - Option row (click target), hover
@media (hover: hover) .item:hover .radio[data-checked]:not([data-disabled]) - Radio circle, focus
.radio:focus-visible - Radio circle
.radio[data-checked] - Fieldset container
.fieldset[data-invalid] .radio:not([data-checked]) - Selected dot
.dot - text
.text - Option description
.itemDescription - Option row (click target)
.item:has([data-disabled]) - Option row (click target)
.item:has([data-disabled]) .itemDescription - Radio circle
.radio[data-disabled] - Selected dot
.dot[data-disabled]
Motion and interaction
RadioGroup animates on hover and check. Try it in the preview at the top.
| Part | When | What changes | Duration | Easing |
|---|---|---|---|---|
| RadioGroup | Hover | Background and border | motion.duration.instant75 ms | motion.easing.standardsettles in place |
| Radio | Hover | Border and background | motion.duration.instant75 ms | motion.easing.standardsettles in place |
| Radio | Check | Background, border and movement | motion.duration.fast150 ms | motion.easing.enterstarts fast, lands softly |
With reduced motion turned on, every change happens at once.
From the code Generated from the component, not editable here.
Props
Parsed from the TypeScript types in packages/react/src/components/RadioGroup/RadioGroup.tsx, so this table cannot drift from the code.
| Prop | Type | Default | Description |
|---|---|---|---|
labelrequired | ReactNode | none | Group label, rendered as the fieldset legend. Ask the question the options answer. |
childrenrequired | ReactNode | none | Radio items. |
description | ReactNode | none | Help under the legend. |
error | ReactNode | none | Error message for the group. Marks it invalid and is linked with aria-describedby. |
value | string | none | No description in the code yet. |
defaultValue | string | none | No description in the code yet. |
onValueChange | (value: string) => void | none | No description in the code yet. |
orientation | 'vertical' | 'horizontal' | 'vertical' | Default `vertical`. Use `horizontal` only for two or three short options. |
name | string | none | No description in the code yet. |
disabled | boolean | none | No description in the code yet. |
readOnly | boolean | none | No description in the code yet. |
required | boolean | none | No description in the code yet. |
hideLabel | boolean | false | Visually hide the legend (it stays the accessible name). |
className | string | none | No description in the code yet. |
Playground
Change the props and see the real RadioGroup update. Controls are generated from the TypeScript props; nothing here changes the system.
<RadioGroup defaultValue="squash" label="Merge strategy" />Workbench
For contributors: every state with the accessibility panel, in the component workbench (Storybook). The stories double as visual test fixtures; the guidance on this page is the user documentation (open RadioGroup in Storybook).
Embed Storybook stories here (4)
Loads from https://bridge-storybook.floriangouloubi.com (set STORYBOOK_URL at build time). If Storybook is not running, the frames stay empty.
Default
Horizontal
Invalid
AllStates
From the code Generated from the component, not editable here.
Changelog
- New
SegmentedControl: pick one of two to five views or modes in a row of attached buttons (Base UI RadioGroup with native button radios)
v1.0.0, Nov 11, 2026