---
title: Radio Group
description: A set of checkable buttons—known as radio buttons—where no more than one of the buttons can be checked at a time.
icon: circle
author:
  name: imskyleen
  url: https://github.com/imskyleen
search:
  keywords: [radio button, options]
related:
  - /choicebox
  - /checkbox
mode: wide
---

<PoweredBy
  packages={[
    { name: 'Base UI', url: 'https://base-ui.com/react/components/radio-group' },
    { name: 'Shadcn UI', url: 'https://ui.shadcn.com/docs/components/radio-group' },
  ]}
/>

### Installation

```package-install
npx shadcn@latest add @redpanda/radio-group
```

<ComponentPreview name="radio-group-demo" description="Default vertical stack of radio items with labels." />

### Horizontal

<ComponentPreview
  name="radio-group-horizontal-demo"
  description="Pass `orientation='horizontal'` to lay items out in a row — useful for size / quantity pickers."
/>

### Disabled items

<ComponentPreview
  name="radio-group-disabled-demo"
  description="Disable individual items with the `disabled` prop — the rest of the group stays interactive."
/>

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

```mermaid
graph TD;
    A[Need user selection?] --> B{Multiple options available?};
    A --> C[Use other input types];
    B --> D{Multiple selections allowed?};
    B --> E[Single option only - use hidden input];
    D --> F[Use Checkbox Group];
    D --> G{Single selection required};
    G --> I{2-5 options?};
    G --> J{Many options 6+?};
    I --> K[Use Radio Group];
    J --> L[Use Select component];

    classDef recommended fill:#4ade80,stroke:#22c55e,stroke-width:2px,color:#000;
    classDef discouraged fill:#fbbf24,stroke:#f59e0b,stroke-width:2px,color:#000;
    classDef decision fill:#60a5fa,stroke:#3b82f6,stroke-width:2px,color:#fff;
    classDef neutral fill:#9ca3af,stroke:#6b7280,stroke-width:2px,color:#fff;

    class F,K,L recommended;
    class E discouraged;
    class A,B,D,G,I,J decision;
    class C neutral;
```

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

```
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

<div className="flex flex-row gap-x-3">
  <ExternalLink href="https://base-ui.com/react/components/radio-group" text="Docs" />
  <ExternalLink href="https://base-ui.com/react/components/radio-group#api-reference" text="API Reference" />
</div>

### 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

<ComponentPreview name="radio-group-demo" />

### Attached

<ComponentPreview
  name="radio-group-attached-demo"
  description="Joined vertical choices use the attached variant with Label surfaces, including narrow layouts."
/>

### With description

<ComponentPreview
  name="radio-group-with-description-demo"
  description="Radio group with per-option secondary description text for plan/tier selection."
/>

### Error state

<ComponentPreview
  name="radio-group-error-demo"
  description="Radio group displaying a validation error at the group level."
/>

### Card style

<ComponentPreview
  name="radio-card-demo"
  description="Card-styled radio group with icon, title, and description per option."
/>

### Form

<ComponentPreview name="radio-group-form-demo" description="A radio group component used in a 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](https://base-ui.com/react/components/radio-group) for the radio group component.
- We take our inspiration from [Shadcn UI](https://ui.shadcn.com/docs/components/radio-group) for the radio group style.
