Radio group
Picks exactly one option from a short list where all are visible.
Examples
Plan
Content
labeldefault
label and description
radio only
States
rest
hover
focus-visible
chosen
disabled
invalid
Usage
When to use it
- Use
- The person picks exactly one of 2 to 6 options that are worth seeing together.
- Use
- Options differ in what they cost or do and each needs a line of description.
- Not for
- More than 6 options. Use Select.
- Not for
- Any number of the options may apply. Use Checkbox.
- Not for
- Switching between views of the same data. Use Segmented control.
Rules
6 rules
- Must
- Name the group with aria-labelledby pointing at its visible question. Because the group is a radiogroup with no name, so a screen reader announces the options without the question.
- Should
- Pass defaultValue when one option is right for most people; leave it empty only when a wrong default costs money or data.
- Must
- Put the difference between options in description, not in a tooltip or a link. Because description is tied by aria-describedby and read while arrowing, while a tooltip never opens on touch.
- Should
- Use the same grammatical form and length for every option label, 1 to 3 words.
- Never
- Build plan cards with a radio in the corner. Use RadioGroupOption rows with descriptions. Because only the 16px circle is the target and the card text is not read with the radio, so the card looks clickable but is not.
- Should
- Switch to Select past 6 options.
Accessibility
5 checks
- The group is role="radiogroup", named by aria-labelledby, with one tab stop.
- Tab focuses the chosen option or the first; arrows move focus and the choice to the next enabled option, wrapping and following visual order in RTL.
- Space chooses a clear option.
- The description is read once through aria-describedby; the whole row is the target.
- A bare RadioGroupItem needs aria-label or a wrapping label.
Edge cases
8 edge cases
- Long label and description
- Both wrap; the circle stays aligned to the first line.
- Right to left
- The circle sits at the start (right).
- Required and invalid
- Rings every circle; the error sits under the group, only after a submit.
- On the inverse surface
- Ink and paper flip.
- One option
- Cannot be cleared again; use a Checkbox.
- Zero options
- An empty group the keyboard skips.
- Held arrow key
- Each repeat moves the choice one option, wrapping.
- Inside a form
- Submits through name.
API
7 props
RadioGroup value, defaultValueunknownRadioGroup onValueChange(value, details) => voidRadioGroup aria-invalidbooleandefaultfalseRadioGroup disabled, readOnly, required, nameBase UIRadioGroupOption labelReactNodedefaultrequiredRadioGroupOption descriptionReactNodeRadioGroupOption, RadioGroupItem value, disabledBase UI Radio
Tokens
13 tokens
--surface-page- var(--paper)
--border-control- oklch(var(--ink-c) / 0.50)
--fail-line- oklch(60% 0.236 26)
--radius-full- 999px
--ink- oklch(var(--ink-c))
--paper- oklch(var(--paper-c))
--surface-pressed- oklch(var(--ink-c) / 0.08)
--text-disabled- oklch(var(--ink-c) / 0.40)
--type-label- 400 14px/16px var(--font-sans)
--type-body-sm- 380 13px/18px var(--font-sans)
--text-1- var(--ink)
--text-2- oklch(var(--ink-c) / 0.80)
--ease-step- cubic-bezier(0.2, 0, 0, 1)