Radio (and RadioGroup) — spec¶
Synthesized from Ant Design
Radio, MUIRadio/RadioGroup, shadcn-uiradio-group. Mutually exclusive choice control. See alsocomponent-form-controls.md.
Radio vs Select¶
Radio for 2-5 visible options. Select / Combobox for 6+ OR when space is constrained.
| Radio | Select | |
|---|---|---|
| All options visible | Yes | No (one at a time) |
| Use | Few visible options; explicit choice | Many options; chosen value visible |
| Visual | Each option as a row | Compact dropdown |
Anatomy¶
API¶
<RadioGroup value={method} onValueChange={setMethod}>
<RadioGroup.Item value="card" id="card" />
<label htmlFor="card">Credit card</label>
<RadioGroup.Item value="kakao" id="kakao" />
<label htmlFor="kakao">KakaoPay</label>
<RadioGroup.Item value="toss" id="toss" />
<label htmlFor="toss">Toss</label>
</RadioGroup>
| Prop | Type | Default | Description |
|---|---|---|---|
value |
string |
controlled | Selected value |
defaultValue |
string |
— | Uncontrolled initial |
onValueChange |
(value: string) => void |
— | Callback |
disabled |
boolean |
false |
Disable all items |
required |
boolean |
false |
One must be selected |
orientation |
"vertical" \| "horizontal" |
"vertical" |
Layout |
States¶
| State | Visual |
|---|---|
| Unselected | Empty circle with border |
| Selected | Filled circle with brand-color dot inside |
| Hover | Border color shift |
| Focus-visible | Ring around the focused item |
| Disabled | Reduced opacity |
Layouts¶
Vertical (default)¶
Horizontal¶
For 2-3 short options.
Card-style (visual-rich)¶
┌─────────────────────┐ ┌─────────────────────┐
│ ● KakaoPay │ │ ○ Toss │
│ No fees │ │ 1.5% fee │
└─────────────────────┘ └─────────────────────┘
Whole card clickable; radio dot in corner OR replaced by border highlight.
Tokens consumed¶
--radio-bg-unchecked
--radio-bg-checked-dot (brand color dot inside)
--radio-border
--radio-border-checked
--radio-error-border
--radio-size (typically 16-20px)
--space-sm (item gap)
--motion-fast
Accessibility¶
RadioGroup:role="radiogroup"+aria-labelledby(often referencing a heading).Radio.Item:role="radio"+aria-checked.- Keyboard:
- Tab moves into the group (focuses the selected one or first if none).
- Arrow keys navigate within the group AND change selection.
- Space activates if not already activated.
- For required:
aria-required="true"on group; show error if no selection on submit. - Label every radio with linked
<label htmlFor>.
Code example — Korean fintech payment method¶
function PaymentMethodPicker({ value, onChange }: Props) {
return (
<fieldset>
<legend>결제 수단</legend>
<RadioGroup value={value} onValueChange={onChange}>
<div className="radio-row">
<RadioGroup.Item value="kakao" id="pay-kakao" />
<label htmlFor="pay-kakao">
<Image src="/kakaopay-logo.svg" alt="KakaoPay" />
카카오페이
</label>
</div>
<div className="radio-row">
<RadioGroup.Item value="naver" id="pay-naver" />
<label htmlFor="pay-naver">
<Image src="/naverpay-logo.svg" alt="NaverPay" />
네이버페이
</label>
</div>
<div className="radio-row">
<RadioGroup.Item value="toss" id="pay-toss" />
<label htmlFor="pay-toss">
<Image src="/toss-logo.svg" alt="Toss" />
토스페이
</label>
</div>
</RadioGroup>
</fieldset>
);
}
Don't¶
- Don't use Radio for binary on/off — use Switch (immediate effect) or Checkbox (form).
- Don't allow zero selection in a required group. Validation must catch it.
- Don't make the visual circle smaller than 16x16. Touch target ≥ 44pt via padding.
- Don't omit
<fieldset>+<legend>for the group context. - Don't have only one radio in a group. That's a Checkbox or just a single button.
References¶
- Ant:
Radio - MUI:
Radio - shadcn-ui:
radio-group - WAI-ARIA: Radio Group pattern
Cross-reference¶
examples/component-form-controls.md— combined with Switch + Checkboxexamples/component-checkbox.mdexamples/component-payment-method-selector.md— KR payment specialization