Skip to content

Radio

A single-select control for mutually exclusive choices. Radio buttons always appear in groups — a lone Radio button is always wrong.

When to use: Mutually exclusive options where all choices are visible at once (plan selection, gender, sort order, payment method).

When not to use: Long lists of options (10+) — use a select/picker. Boolean true/false that takes immediate effect — use Switch. Multi-select — use Checkbox.

import { Radio } from '@/components';
const [plan, setPlan] = useState('monthly');
<Radio value="monthly" selectedValue={plan} onSelect={setPlan} label="Monthly — $9/mo" />
<Radio value="annual" selectedValue={plan} onSelect={setPlan} label="Annual — $79/yr (save 27%)" />
<Radio value="lifetime" selectedValue={plan} onSelect={setPlan} label="Lifetime — $299" />
PropTypeDefaultDescription
valuestringThis option’s value (required)
selectedValuestringCurrently selected value (required)
onSelect(value: string) => voidSelection handler (required)
labelstringVisible text label
isDisabledbooleanfalsePrevents interaction
testIDstringFor testing

Do: Always render 2+ Radio buttons sharing the same state.

const [sort, setSort] = useState('newest');
<Radio value="newest" selectedValue={sort} onSelect={setSort} label="Newest first" />
<Radio value="oldest" selectedValue={sort} onSelect={setSort} label="Oldest first" />
<Radio value="popular" selectedValue={sort} onSelect={setSort} label="Most popular" />

Don’t: Render a single Radio button.

// ✗ A single radio is meaningless — what's the alternative?
<Radio value="agree" selectedValue={agreed} onSelect={setAgreed} label="I agree" />
// ✓ Use Checkbox for a single boolean
<Checkbox checked={agreed} onToggle={setAgreed} label="I agree to the terms" />
  • Role: radio
  • State: selected / unselected
  • Label is announced as the accessible name
  • Checkbox — multi-select boolean
  • Switch — immediate-effect boolean toggle