# 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 best Includes 100 messages per month Includes 200 messages per month Unlimited 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 ```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 projects Advanced reporting Up 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 teams Advanced reporting and analytics Share 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 teams Advanced reporting and analytics Share 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 (
{ e.preventDefault(); const formData = new FormData(e.currentTarget); const value = formData.get("plan-validation"); setMessage(`Your chosen plan is: ${value}`); }} > For side projects and small teams Advanced reporting and analytics Share access with up to 10 teammates Choose a subscription before continuing. {!!message &&

{message}

}
); } ``` ### 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 teams Advanced reporting and analytics Share 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 background Another option with primary styling

Secondary variant

Lower emphasis variant for use in surfaces Another 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 best Includes 100 messages per month Includes 200 messages per month Unlimited 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 best Includes 100 messages per month Includes 200 messages per month Unlimited 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 |