Skip to content
Redpanda UI
Esc
↑↓navigate↵open⌘Jpreview
Made by imskyleen
Edit on GitHub

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

bash
npx shadcn@latest add @redpanda/radio-group
bash
pnpm dlx shadcn@latest add @redpanda/radio-group
bash
yarn dlx shadcn@latest add @redpanda/radio-group
bash
bunx shadcn@latest add @redpanda/radio-group
bash
nubx shadcn@latest add @redpanda/radio-group
bash
aube dlx shadcn@latest add @redpanda/radio-group

Horizontal

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

tsx
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

tsx
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:

plaintext
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 spacing

Key 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 item role="radio" with aria-checked. Base UI additionally sets the Radix-compat data-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).
  • onValueChange narrows Base UI’s unknown value to string and forwards Base UI’s eventDetails as an optional second argument, so the Radix-style single-argument callback keeps working while event/reason inspection remains available.

Credits

  • We use Base UI for the radio group component.
  • We take our inspiration from Shadcn UI for the radio group style.

Recent changes

  • patchv1.2.0Pin shipped dependency floors to the version we develop against. Registry items now declare ranges like `^5.1.9` (the actual installed version) instead of collapsing to `^5.0.0`, so consumers start on the known-tested baseline while caret semantics still allow any compatible release within the same major.#133
  • minorv1.1.0Theme docs refresh, readability pass on semantic foregrounds, and consumer-facing Base UI regression fixes.#121
  • minorv1.0.0Post-Base-UI polish. Public API unchanged.#116
  • majorv1.0.0Migrate every Radix-based primitive to `@base-ui/react@^1.4.0` (Base UI).#114
  • patchv0.3.1## 🚧 Updated#112
See full history →

Last updated on

Was this page helpful?