Radio Group
A set of checkable buttons—known as radio buttons—where no more than one of the buttons can be checked at a time.
Installation
npx shadcn@latest add @redpanda/radio-grouppnpm dlx shadcn@latest add @redpanda/radio-groupyarn dlx shadcn@latest add @redpanda/radio-groupbunx shadcn@latest add @redpanda/radio-groupnubx shadcn@latest add @redpanda/radio-groupaube dlx shadcn@latest add @redpanda/radio-groupHorizontal
Disabled items
When to use
Radio groups provide mutually exclusive options where users must select exactly one choice. Use this decision tree to determine when radio groups are appropriate:
Usage
Basic Usage
import { RadioGroup, RadioGroupItem } from '@/components/redpanda-ui/radio-group';import { Label } from '@/components/redpanda-ui/label';
<RadioGroup defaultValue="option1"> <div className="flex items-center space-x-2"> <RadioGroupItem value="option1" id="r1" /> <Label htmlFor="r1">Option 1</Label> </div> <div className="flex items-center space-x-2"> <RadioGroupItem value="option2" id="r2" /> <Label htmlFor="r2">Option 2</Label> </div> <div className="flex items-center space-x-2"> <RadioGroupItem value="option3" id="r3" /> <Label htmlFor="r3">Option 3</Label> </div></RadioGroup>;Controlled Usage
import { useState } from 'react';
function ControlledRadioGroup() { const [value, setValue] = useState('option1');
return ( <RadioGroup value={value} onValueChange={setValue}> <div className="flex items-center space-x-2"> <RadioGroupItem value="option1" id="r1" /> <Label htmlFor="r1">Option 1</Label> </div> <div className="flex items-center space-x-2"> <RadioGroupItem value="option2" id="r2" /> <Label htmlFor="r2">Option 2</Label> </div> </RadioGroup> );}Anatomy
The Radio Group component is built with Base UI primitives:
RadioGroup (Root Container)├── RadioGroupItem (Individual Radio Button)│ ├── Border/Visual styling - circular border│ └── RadioGroupIndicator (Selection Indicator)│ └── Filled circle - shown when checked, fades/scales in via CSS└── Layout - grid with gap-2.5 spacingKey Features:
- Single Selection: Only one option can be selected at a time
- Keyboard Navigation: Arrow keys move between options, Space/Enter selects
- Accessibility: Full ARIA support with proper labeling and focus management
- Animation: Indicator fades and scales in via Base UI CSS transitions
Props
Registry appearance and layout
variant="standard" is the default, with the shared radio-group spacing. Use orientation="horizontal" for a
wrapping row; the default orientation is vertical. Keep native Base UI props such as name, controlled value,
onValueChange, disabled state and event cancellation on the group.
variant="attached" owns joined vertical rows: zero gap, overlapping borders, rounding and shadow. Pair it with
Label surface="attached" for the actual choice surfaces. The attached appearance is vertical-only; TypeScript
rejects combining it with horizontal orientation. Keep width constraints on the real containing layout rather
than overriding the group’s paint or spacing.
For React Hook Form, pass name={field.name}, value={field.value}, onValueChange={field.onChange} and
onBlur={field.onBlur} to the group, and forward its ref to the first actual radio. Give the group a label and describe each invalid radio with the rendered group error
ID. An invalid submit should focus that radio, not a non-interactive wrapper. The form example demonstrates this
complete contract and preserves native form submission values.
Examples
Default
Attached
With description
Error state
Card style
Form
Keyboard interactions
| Key | Action |
|---|---|
Tab into the group |
Focus lands on the currently-selected item, or the first item if none is selected. |
ArrowUp / ArrowDown |
Moves between items (vertical). |
ArrowLeft / ArrowRight |
Moves between items (horizontal). |
Space |
Selects the focused item. |
Accessibility
- The group container is
role="radiogroup"and each itemrole="radio"witharia-checked. Base UI additionally sets the Radix-compatdata-state="checked" | "unchecked"attribute. - Always label the group: wrap it in a
<Label>+ visual heading, or use<Field>/<FormLabel>in a form context. Individual items also need associated<Label htmlFor>. - Focus is roving — only the currently-selected item is tabbable. Arrow keys both move focus and select the next item (standard radio group semantics).
onValueChangenarrows Base UI’sunknownvalue tostringand forwards Base UI’seventDetailsas an optional second argument, so the Radix-style single-argument callback keeps working while event/reason inspection remains available.