# RadioGroup
**Category**: react
**URL**: https://www.heroui.com/docs/react/components/radio-group
**Source**: https://raw.githubusercontent.com/heroui-inc/heroui/refs/heads/v3/apps/docs/content/docs/react/components/(forms)/radio-group.mdx
> Radio group for selecting a single option from a list
***
## Import
```tsx
import { RadioGroup, Radio } from '@heroui/react';
```
### Usage
```tsx
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
export function Basic() {
return (
Choose the plan that suits you bestIncludes 100 messages per monthIncludes 200 messages per monthUnlimited messages
);
}
```
### Anatomy
Import the RadioGroup component and access all parts using dot notation.
```tsx
import {RadioGroup, Radio, Label, Description, FieldError} from '@heroui/react';
export default () => (
✓ {/* Custom indicator (optional) */}
)
```
### Custom Indicator
```tsx
"use client";
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
export function CustomIndicator() {
return (
Choose the plan that suits you best
{({isSelected}) =>
isSelected ? ✓ : null
}
Includes 100 messages per month
{({isSelected}) =>
isSelected ? ✓ : null
}
Includes 200 messages per month
{({isSelected}) =>
isSelected ? ✓ : null
}
Unlimited messages
);
}
```
### Horizontal Orientation
```tsx
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
export function Horizontal() {
return (
For side projectsAdvanced reportingUp to 10 teammates
);
}
```
### Controlled
```tsx
"use client";
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
import React from "react";
export function Controlled() {
const [value, setValue] = React.useState("pro");
return (
For side projects and small teamsAdvanced reporting and analyticsShare access with up to 10 teammates
Selected plan: {value}
);
}
```
### Uncontrolled
Combine `defaultValue` with `onChange` when you only need to react to updates.
```tsx
"use client";
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
import React from "react";
export function Uncontrolled() {
const [selection, setSelection] = React.useState("pro");
return (
setSelection(nextValue)}
>
For side projects and small teamsAdvanced reporting and analyticsShare access with up to 10 teammates
Last chosen plan: {selection}
);
}
```
### Validation
```tsx
"use client";
import {Button, Description, FieldError, Form, Label, Radio, RadioGroup} from "@heroui/react";
import React from "react";
export function Validation() {
const [message, setMessage] = React.useState(null);
return (
);
}
```
### Disabled
```tsx
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
export function Disabled() {
return (
Plan changes are temporarily paused while we roll out updates.For side projects and small teamsAdvanced reporting and analyticsShare access with up to 10 teammates
);
}
```
### 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
```tsx
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
export function Variants() {
return (
Primary variant
Standard styling with default backgroundAnother option with primary styling
Secondary variant
Lower emphasis variant for use in surfacesAnother option with secondary styling
);
}
```
### In Surface
When used inside a [Surface](/docs/components/surface) component, use `variant="secondary"` to apply the lower emphasis variant suitable for surface backgrounds.
```tsx
import {Description, Label, Radio, RadioGroup, Surface} from "@heroui/react";
export function OnSurface() {
return (
Choose the plan that suits you bestIncludes 100 messages per monthIncludes 200 messages per monthUnlimited messages
);
}
```
### Delivery & Payment
## Related Components
- **Fieldset**: Group related form controls with legends
- **Surface**: Base container surface
- **Description**: Helper text for form fields
### Custom Render Function
```tsx
"use client";
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
export function CustomRenderFunction() {
return (
}
>
Choose the plan that suits you bestIncludes 100 messages per monthIncludes 200 messages per monthUnlimited messages
);
}
```
## Styling
### Passing Tailwind CSS classes
```tsx
import { RadioGroup, Radio } from '@heroui/react';
export default () => (
Basic Plan
Premium Plan
Business Plan
);
```
### Customizing the component classes
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-primary;
}
.radio__indicator {
@apply bg-primary;
}
.radio__content {
@apply gap-1;
}
}
```
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
- `.radio-group` - Base radio group container
- `.radio` - Individual radio item
- `.radio__control` - Radio control (circular button)
- `.radio__indicator` - Radio indicator (inner dot)
- `.radio__content` - Radio content wrapper
#### Modifier Classes
- `.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 Props
| 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` | - | Overrides the default DOM element with a custom render function.|
### Radio Props
| 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: RadioRenderProps) => React.ReactNode` | - | Radio content or render prop |
| `render` | `DOMRenderFunction` | - | Overrides the default DOM element with a custom render function.|
### Radio.Control Props
Extends `React.HTMLAttributes`.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `children` | `React.ReactNode` | - | The content to render inside the control wrapper (typically Radio.Indicator) |
### Radio.Indicator Props
Extends `React.HTMLAttributes`.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `children` | `React.ReactNode \| (values: RadioRenderProps) => React.ReactNode` | - | Optional content or render prop that receives the current radio state. |
### Radio.Content Props
Extends `React.HTMLAttributes`.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `children` | `React.ReactNode` | - | The content to render inside the content wrapper (typically Label and Description) |
### RadioRenderProps
When using the render prop pattern, these values are provided:
| Prop | Type | Description |
|------|------|-------------|
| `isSelected` | `boolean` | Whether the radio is currently selected |
| `isHovered` | `boolean` | Whether the radio is hovered |
| `isPressed` | `boolean` | Whether the radio is currently pressed |
| `isFocused` | `boolean` | Whether the radio is focused |
| `isFocusVisible` | `boolean` | Whether the radio is keyboard focused |
| `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 |