RadioGroup
Radio group for selecting a single option from a list
Import
import { RadioGroup, Radio } from '@heroui/react';Usage
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
export function Basic() {
return (
<RadioGroup defaultValue="premium" name="plan">Anatomy
Import the RadioGroup component and access all parts using dot notation.
import {RadioGroup, Radio, Label, Description, FieldError} from '@heroui/react';
export default () => (
<RadioGroup>
<Label />
<Description />
<Radio value="option1">
<Radio.Control>
<Radio.Indicator>
<span>✓</span> {/* Custom indicator (optional) */}
</Radio.Indicator>
</Radio.Control>
<Radio.Content>
<Label />
<Description />
</Radio.Content>
</Radio>
<FieldError />
</RadioGroup>
)Custom Indicator
"use client";
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
export function CustomIndicator() {Horizontal Orientation
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
export function Horizontal() {
return (
<div className="flex flex-col gap-4">Controlled
Selected plan: pro
"use client";
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
import React from "react";
Uncontrolled
Combine defaultValue with onChange when you only need to react to updates.
Last chosen plan: pro
"use client";
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
import React from "react";
Validation
"use client";
import {Button, Description, FieldError, Form, Label, Radio, RadioGroup} from "@heroui/react";
import React from "react";
Disabled
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
export function Disabled() {
return (
<RadioGroup isDisabled defaultValue="pro" name="plan-disabled">On Surface
When used inside a Surface component, RadioGroup automatically applies on-surface styling.
import {Description, Label, Radio, RadioGroup, Surface} from "@heroui/react";
export function OnSurface() {
return (
<Surface className="w-full rounded-3xl p-6">Delivery & Payment
import {Description, Label, Radio, RadioGroup} from "@heroui/react";
import {Icon} from "@iconify/react";
import clsx from "clsx";
export function DeliveryAndPayment() {Styling
Passing Tailwind CSS classes
import { RadioGroup, Radio } from '@heroui/react';
export default () => (
<RadioGroup defaultValue="premium" name="plan">
<Radio
className="border-border group cursor-pointer rounded-xl border-2 p-4 hover:border-blue-300 data-[selected=true]:border-blue-500 data-[selected=true]:bg-blue-500/10"
value="basic"
>
<Radio.Indicator className="border-border border-2 group-hover:border-blue-400 group-data-[selected=true]:border-blue-500 group-data-[selected=true]:bg-blue-500" />
Basic Plan
</Radio>
<Radio
className="border-border group cursor-pointer rounded-xl border-2 p-4 hover:border-purple-300 data-[selected=true]:border-purple-500 data-[selected=true]:bg-purple-500/10"
value="premium"
>
<Radio.Indicator className="border-border border-2 group-hover:border-purple-400 group-data-[selected=true]:border-purple-500 group-data-[selected=true]:bg-purple-500" />
Premium Plan
</Radio>
<Radio
className="border-border group cursor-pointer rounded-xl border-2 p-4 hover:border-emerald-300 data-[selected=true]:border-emerald-500 data-[selected=true]:bg-emerald-500/10"
value="business"
>
<Radio.Indicator className="border-border border-2 group-hover:border-emerald-400 group-data-[selected=true]:border-emerald-500 group-data-[selected=true]:bg-emerald-500" />
Business Plan
</Radio>
</RadioGroup>
);Customizing the component classes
To customize the RadioGroup component classes, you can use the @layer components directive.
Learn more.
@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 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):
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:
:hoveror[data-hovered="true"](border color changes) - Focus:
:focus-visibleor[data-focus-visible="true"](shows focus ring) - Pressed:
:activeor[data-pressed="true"](scale transform) - Disabled:
:disabledor[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 |
isOnSurface | boolean | false | Whether the radio group is displayed on a surface (affects styling) |
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 |
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 |
Radio.Control Props
Extends React.HTMLAttributes<HTMLSpanElement>.
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | - | The content to render inside the control wrapper (typically Radio.Indicator) |
Radio.Indicator Props
Extends React.HTMLAttributes<HTMLSpanElement>.
| 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<HTMLDivElement>.
| 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 |





