RadioGroup

Accessible radio button group with controlled/uncontrolled support and roving tab index.

RadioGroup

The RadioGroup component provides a fully accessible radio button group following WAI-ARIA patterns. It supports controlled and uncontrolled modes, keyboard navigation via arrow keys, and proper focus management.

RadioGroup requires sub-components: RadioGroupItem for each option and RadioGroupIndicator to render the visual indicator.


Features

  • Controlled/uncontrolled modes
  • Roving tab index for arrow-key navigation (Up/Down, Left/Right)
  • Full keyboard support (Home, End, Tab)
  • ARIA radiogroup semantics
  • Optional looping when navigating
  • RTL support via dir prop

Usage

import { RadioGroup, RadioGroupItem, RadioGroupIndicator } from '@loke/ui/radio-group';

export default function Example() {
  const [value, setValue] = React.useState('option-1');

  return (
    <RadioGroup value={value} onValueChange={setValue}>
      <div className="flex items-center gap-3">
        <RadioGroupItem value="option-1" id="opt1">
          <RadioGroupIndicator />
        </RadioGroupItem>
        <label htmlFor="opt1">Option 1</label>
      </div>
      <div className="flex items-center gap-3">
        <RadioGroupItem value="option-2" id="opt2">
          <RadioGroupIndicator />
        </RadioGroupItem>
        <label htmlFor="opt2">Option 2</label>
      </div>
    </RadioGroup>
  );
}

Props

RadioGroup

Prop

Type

RadioGroupItem

Prop

Type

RadioGroupIndicator

Prop

Type


Examples

Selected: apple

Horizontal layout

<RadioGroup defaultValue="yes">
  <div className="flex gap-4">
    {['yes', 'no'].map((opt) => (
      <div key={opt} className="flex items-center gap-2">
        <RadioGroupItem value={opt} id={opt}>
          <RadioGroupIndicator className="w-4 h-4 bg-blue-600 rounded-full" />
        </RadioGroupItem>
        <label htmlFor={opt} className="capitalize">{opt}</label>
      </div>
    ))}
  </div>
</RadioGroup>

Vertical layout with cards

<RadioGroup defaultValue="plan-1">
  <div className="space-y-3">
    <div className="flex items-start gap-3 p-3 border rounded cursor-pointer hover:bg-gray-50">
      <RadioGroupItem value="plan-1" id="p1">
        <RadioGroupIndicator className="w-5 h-5 border-2 border-gray-300 rounded-full" />
      </RadioGroupItem>
      <label htmlFor="p1" className="flex-1">
        <div className="font-semibold">Basic Plan</div>
        <div className="text-sm text-gray-600">$9/month</div>
      </label>
    </div>
  </div>
</RadioGroup>

Keyboard Navigation

  • Arrow Up/Down (vertical) or Arrow Left/Right (horizontal): Move focus between items
  • Home: Jump to first item
  • End: Jump to last item
  • Space/Enter: Select focused item
  • Tab: Exit the group (only one item has tabindex=0)

Accessibility

  • Group has role="radiogroup"
  • Items have role="radio" and aria-checked
  • Items support aria-label and aria-labelledby for accessible names
  • Disabled items have aria-disabled="true"
  • Roving tab index ensures keyboard-friendly navigation

Best practices

  • Always provide labels (visible or via aria-label)
  • Use appropriate orientation; vertical for lists, horizontal for quick toggles
  • Disable the entire group (via disabled) rather than individual items when possible
  • For forms, use the name prop to ensure proper form submission