RadioGroup
Accessible radio button group with legend and validation; with startLabel and endLabel it is a rating scale.
Status: beta. Group: form. Markdown version: /design-system/components/radio-group.md
When to use
RadioGroup is the standard way to ask for an exclusive choice: it renders a fieldset legend, the labelled radios, helper and error text, and manages the selection in controlled or uncontrolled mode.
When not to use
- Do not use radios for actions or navigation; picking an option must not immediately commit anything.
Look-alikes
- CheckboxGroup: Do not use it for exclusive choices; that is RadioGroup.
- Select: Use Select for five or more exclusive options, or when space is tight; RadioGroup is better when all options should stay visible.
- SelectableCard: For bare text options without supporting content, use RadioGroup or CheckboxGroup.
- SelectableCardGroup: Do not use rich cards when plain RadioGroup or CheckboxGroup labels communicate the choices clearly.
Props
| Prop | Type | Default | Description | Required |
|---|---|---|---|---|
name | string | none | Native form field name shared by the set; auto-generated when omitted. | No |
legend | string | none | Group label rendered as the fieldset legend. | Yes |
options | RadioGroupOption[] | none | Radio options; per-option disabled keeps the choice visible. | Yes |
value | string | none | Controlled selected value; "" is treated as absent (uncontrolled) | No |
defaultValue | string | none | Initial value when uncontrolled. | No |
onValueChange | (value: string) => void | none | Fired with the next value when selection changes. | No |
orientation | "horizontal" | "vertical" | vertical | Stack direction. | No |
size | "sm" | "md" | "lg" | "S" | "M" | "L" | md | Radio control size. | No |
disabled | boolean | false | Disables the whole set. | No |
error | string | none | Error message; announces via role=alert and sets aria-invalid. | No |
helperText | string | none | Helper copy below the group; always rendered, alongside error when both are set. | No |
startLabel | string | none | Anchor shown before the first option, for rating scales ("Serious" in Serious 1 to 5 Playful). Announced paired with its option, not on its own. | No |
endLabel | string | none | Anchor shown after the last option; pairs with startLabel. | No |
hideLegend | boolean | false | Keeps the legend as the accessible name but hides it visually. | No |
className | string | none | Merged onto the fieldset. | No |
Variants
- Available orientations:
vertical,horizontal(defaultvertical). Forwarded to fieldset layout class in RadioGroup.module.css. - Available sizes:
sm,md,lg,S,M,L(defaultsm).
Invalid combinations
- MUST NOT:
valueanddefaultValueset together.valuemakes the component controlled anddefaultValueuncontrolled; pass one, not both.
Keyboard
The contract records no keyboard interaction.
Rules
Every item under When not to use is a MUST NOT. MUST and SHOULD follow RFC 2119.
| Level | Rule | How it is checked |
|---|---|---|
| MUST NOT | value and defaultValue set together. value makes the component controlled and defaultValue uncontrolled; pass one, not both. | Checked by the MCP tool validate_component_usage. |
| MUST | Required stories produce no axe violations at the configured severity. | Automated by npm run test:stories:matrix:ci. WCAG 2.5.8. |
| MUST | The accessibility tree (role, name, state) matches the committed snapshot for every required story. | Automated by npm run test:stories:matrix:ci. WCAG 1.3.1, 4.1.2. |
| MUST | The component remains legible and operable under a real browser forced-colors: active pass. | Automated by npm run test:stories:hc. |
| MUST | A human completed an end-to-end accessibility review of this component. | Manual review, last on 2026-05-28. |
| SHOULD | Write the legend as the question (Delivery speed); the options then read as answers. | Not stated in the contract. |
| SHOULD | Use it whenever all options should be visible at once; switch to Select when space is tight or options exceed about seven. | Not stated in the contract. |
| SHOULD | Preselect the safest common option with defaultValue when a recommendation exists. | Not stated in the contract. |
| SHOULD | Use orientation=horizontal only for a few short options that still fit one line on mobile. | Not stated in the contract. |
| SHOULD | For a rating scale, give numbered options with startLabel and endLabel (Serious 1 to 5 Playful) and hideLegend when the anchors already say what is rated; the legend stays the accessible name. | Not stated in the contract. |
| SHOULD NOT | Do not leave a required group unselected without a recovery plan; users cannot un-pick a radio once one is chosen. | Not stated in the contract. |
| MUST NOT | Maintainers: Do not bypass design tokens or skip forced-colors verification at beta. | Release review; not machine-checked. |
Install and import
- Install:
npm install @digitaltableteur/react(version 0.9.1, restricted access on npm). - Import:
import { RadioGroup } from "@digitaltableteur/react"; - Entry point:
@digitaltableteur/react/formsexports it too. - Styles: import
@digitaltableteur/react/style.cssonce at the app boundary. - Also available as the custom element
<dt-radio-group>; see the custom elements manifest.
Links
- Machine-readable contract (Design System Contract 1.0)
- Markdown version of this page
- Component index as JSON
- All components
- Source: The implementation lives in a private repository. This page, its Markdown twin and the contract are generated from it at build time and are the public source of truth.