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"andaria-checked - Items support
aria-labelandaria-labelledbyfor 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