Skip to main content

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

PropTypeDefaultDescription
checkedboolean-Controlled checked state
defaultCheckedboolean-Uncontrolled initial state
onChangefunction-Change handler (not called when disabled)
labelstring''Optional label text
classNamestring''Additional classes for root label
variantstring'basic'Visual style: basic, rounded, outline, outline-text
sizestring'md'Size: sm, md, lg
disabledbooleanfalseDisable interaction and adjust styles
namestring-Radio group name
valuestring-Radio value
...propsany-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
  • 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 name attribute
  • 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