Radio
The Radio component provides accessible radio buttons with multiple visual variants, sizes, labels, and disabled state.
Overview
- Variants:
basic,rounded,outline,outline-text - Sizes:
sm,md,lg - Label support
- Controlled and uncontrolled usage
- Disabled state
Basic Usage
import { Radio } from '@/components/atoms'
function MyComponent() {
const [value, setValue] = useState('a')
return (
<>
<Radio name='group' value='a' label='Option A' checked={value === 'a'} onChange={() => setValue('a')} />
<Radio name='group' value='b' label='Option B' checked={value === 'b'} onChange={() => setValue('b')} />
</>
)
}
Props
| Prop | Type | Default | Description |
|---|---|---|---|
checked | boolean | - | Controlled checked state |
defaultChecked | boolean | - | Uncontrolled initial state |
onChange | function | - | Change handler (not called when disabled) |
label | string | '' | Optional label text |
className | string | '' | Additional classes for root label |
variant | string | 'basic' | Visual style: basic, rounded, outline, outline-text |
size | string | 'md' | Size: sm, md, lg |
disabled | boolean | false | Disable interaction and adjust styles |
name | string | - | Radio group name |
value | string | - | Radio value |
...props | any | - | Spread to underlying input[type="radio"] |
Variants
<Radio label='Option A' name='v1' value='a' />
<Radio label='Option A' name='v2' value='a' variant='rounded' />
<Radio label='Option A' name='v3' value='a' variant='outline' />
<Radio label='Option A' name='v4' value='a' variant='outline-text' />
Sizes
<Radio label='Small' name='sz' value='sm' size='sm' />
<Radio label='Medium' name='sz' value='md' size='md' />
<Radio label='Large' name='sz' value='lg' size='lg' />
Disabled
<Radio label='Disabled Radio' name='disabled' value='x' disabled />
Examples (from RadioButtonPage)
// Basic
<Radio label='Option A' checked={selected.basic === 'a'} onChange={() => setSelected((s) => ({ ...s, basic: 'a' }))} name='basic' value='a' />
// Rounded
<Radio label='Option A' variant='rounded' checked={selected.rounded === 'a'} onChange={() => setSelected((s) => ({ ...s, rounded: 'a' }))} name='rounded' value='a' />
// Outline
<Radio label='Option A' variant='outline' checked={selected.outline === 'a'} onChange={() => setSelected((s) => ({ ...s, outline: 'a' }))} name='outline' value='a' />
// Outline with text
<Radio label='Option A' variant='outline-text' checked={selected.outlineText === 'a'} onChange={() => setSelected((s) => ({ ...s, outlineText: 'a' }))} name='outlineText' value='a' />
// Sizes
<Radio label='Small' size='sm' checked={selected.size === 'sm'} onChange={() => setSelected((s) => ({ ...s, size: 'sm' }))} name='size' value='sm' />
Styling
- Root:
.radio(+ composed variants and sizes)- Variants:
.radio-rounded,.radio-outline,.radio-outline-text - Sizes:
.radio-sm,.radio-md,.radio-lg - Disabled:
.disabled
- Variants:
- Hidden native input:
input[type='radio'] - Visual circle:
.radio-custom(checked adds inner white dot) - Label:
.radio-label
Accessibility
- Native radio input for semantics and keyboard support
- Group radios by shared
nameattribute - Ensure descriptive
label
Best Practices
- Use controlled state for predictable behavior
- Keep related radios in the same group (
name) - Provide enough spacing for touch targets