---
title: RadioGroup
description: Radio group for selecting a single option from a list
icon: updated
links:
  rac: RadioField
  source: radio-group/radio-group.tsx
  styles: radio-group.css
  storybook: Components/Forms/RadioGroup
---

## Usage

```tsx
import { RadioGroup, Radio } from '@heroui/react';
```

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

## Anatomy

```tsx
import {RadioGroup, Radio, Label, Description, FieldError} from '@heroui/react';

export default () => (
  <RadioGroup>
    <Label />
    <Description />
    <Radio value="option1">
      <Radio.Content> {/* The clickable area: control + label */}
        <Radio.Control>
          <Radio.Indicator>
            <span>✓</span> {/* Custom indicator (optional) */}
          </Radio.Indicator>
        </Radio.Control>
        Label {/* plain text — the clickable label */}
      </Radio.Content>
      <Description /> {/* Sibling — stays outside the button (announced via aria-describedby) */}
      <FieldError /> {/* Optional — per-radio validation message */}
    </Radio>
    <FieldError /> {/* Optional — group-level validation */}
  </RadioGroup>
)
```

## Examples

### Horizontal Orientation

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

### Variants

The RadioGroup component supports two visual variants:

- **`primary`** (default) - Standard styling with default background, suitable for most use cases
- **`secondary`** - Lower emphasis variant, suitable for use in Surface components

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

### In Surface

When used inside a [Surface](/docs/components/surface) component, use `variant="secondary"` to apply the lower emphasis variant suitable for surface backgrounds.

<ComponentPreview
  name="radio-group-on-surface"
/>

### Disabled

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

### Controlled

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

### Uncontrolled

Combine `defaultValue` with `onChange` when you only need to react to updates.

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

### Validation

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

### Delivery & Payment

<ComponentPreview
  isBgSolid
  align="start"
  name="radio-group-delivery-and-payment"
/>

### Custom Indicator

<ComponentPreview
  name="radio-group-custom-indicator"
/>

### Render Function

<ComponentPreview
  name="radio-group-render-function"
/>

## Customization

### Tailwind CSS

<ComponentPreview name="radio-group-custom-styles" />

### Global CSS

To customize the RadioGroup component classes, you can use the `@layer components` directive.
[Learn more](https://tailwindcss.com/docs/adding-custom-styles#adding-component-classes).

```css
@layer components {
  .radio-group {
    @apply gap-2;
  }

  .radio {
    @apply gap-4 rounded-lg border border-border p-3 hover:bg-surface-hovered;
  }

  .radio__control {
    @apply border-2 border-accent;
  }

  .radio__indicator {
    @apply bg-primary;
  }

  .radio__content {
    @apply gap-1;
  }
}
```

## Styling Reference

HeroUI follows the [BEM](https://getbem.com/) methodology to ensure component variants and states are reusable and easy to customize.

### CSS Classes

The RadioGroup component uses these CSS classes ([View source styles](https://github.com/heroui-inc/heroui/blob/v3/packages/styles/components/radio-group.css)):

#### Base Classes [!toc]

- `.radio-group` - Base radio group container
- `.radio` - Individual radio item
- `.radio__content` - Radio button (RAC label wrapper for control + label text)
- `.radio__control` - Radio control (circular button)
- `.radio__indicator` - Radio indicator (inner dot)

#### Modifier Classes [!toc]

- `.radio--disabled` - Disabled radio state

### Interactive States

The radio supports both CSS pseudo-classes and data attributes for flexibility:

- **Selected**: `[aria-checked="true"]` or `[data-selected="true"]` (indicator appears)
- **Hover**: `:hover` or `[data-hovered="true"]` (border color changes)
- **Focus**: `:focus-visible` or `[data-focus-visible="true"]` (shows focus ring)
- **Pressed**: `:active` or `[data-pressed="true"]` (scale transform)
- **Disabled**: `:disabled` or `[aria-disabled="true"]` (reduced opacity, no pointer events)
- **Invalid**: `[data-invalid="true"]` or `[aria-invalid="true"]` (error border color)

## API Reference

### RadioGroup

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `value` | `string` | - | The current value (controlled) |
| `defaultValue` | `string` | - | The default value (uncontrolled) |
| `onChange` | `(value: string) => void` | - | Handler called when the value changes |
| `isDisabled` | `boolean` | `false` | Whether the radio group is disabled |
| `isRequired` | `boolean` | `false` | Whether the radio group is required |
| `isReadOnly` | `boolean` | `false` | Whether the radio group is read only |
| `isInvalid` | `boolean` | `false` | Whether the radio group is in an invalid state |
| `variant` | `"primary" \| "secondary"` | `"primary"` | Visual variant of the component. `primary` is the default style with shadow. `secondary` is a lower emphasis variant without shadow, suitable for use in surfaces. |
| `name` | `string` | - | The name of the radio group, used when submitting an HTML form |
| `orientation` | `'horizontal' \| 'vertical'` | `'vertical'` | The orientation of the radio group |
| `children` | `React.ReactNode \| (values: RadioGroupRenderProps) => React.ReactNode` | - | Radio group content or render prop |
| `render` | `DOMRenderFunction<keyof React.JSX.IntrinsicElements, RadioGroupRenderProps>` | - | Overrides the default DOM element with a custom render function.|

### Radio

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `value` | `string` | - | The value of the radio button |
| `isDisabled` | `boolean` | `false` | Whether the radio button is disabled |
| `name` | `string` | - | The name of the radio button, used when submitting an HTML form |
| `children` | `React.ReactNode \| (values: RadioFieldRenderProps) => React.ReactNode` | - | Radio content or field render prop |
| `render` | `DOMRenderFunction<keyof React.JSX.IntrinsicElements, RadioFieldRenderProps>` | - | Overrides the default DOM element with a custom render function.|

### Radio.Control

Extends `React.HTMLAttributes<HTMLSpanElement>`.

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `children` | `React.ReactNode` | - | The content to render inside the control wrapper (typically Radio.Indicator) |

### Radio.Indicator

Extends `React.HTMLAttributes<HTMLSpanElement>`.

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `children` | `React.ReactNode \| (values: RadioButtonRenderProps) => React.ReactNode` | - | Optional content or render prop that receives the current radio button state. |

### Radio.Content

The clickable area of the radio (the `<label>` wrapping the hidden input). Place `Radio.Control` and the `Label` inside it. `className` accepts a render function that receives `RadioButtonRenderProps`.

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `children` | `React.ReactNode \| (values: RadioButtonRenderProps) => React.ReactNode` | - | The clickable content (typically Radio.Control and Label) |

### RadioFieldRenderProps

When using a render prop on the root `Radio`, these field-level values are provided:

| Prop | Type | Description |
|------|------|-------------|
| `isSelected` | `boolean` | Whether the radio is currently selected |
| `isDisabled` | `boolean` | Whether the radio is disabled |
| `isReadOnly` | `boolean` | Whether the radio is read only |
| `isInvalid` | `boolean` | Whether the radio is in an invalid state |
| `isRequired` | `boolean` | Whether the radio is required |

### RadioButtonRenderProps

`Radio.Control` and `Radio.Indicator` use button-level render props (`isHovered`, `isPressed`, `isFocusVisible`, etc.). Pass a function as `Radio.Control` children or to `Radio.Indicator` to access them.

## Related Showcases

<RelatedShowcases component="RadioGroup" />

## Related Components

<RelatedComponents component="radiogroup" />
