RadioGroup
Radio group for selecting a single option from a list
Completeness: behavior-required
The stylesheet is only the visible half of this component. It also needs a keyboard
map, focus managed across more than one element, ARIA attributes that point at other
nodes, and [data-*] attributes that only running code sets — so a CSS-only port will
not reproduce it. Use @blakeui/react, or read the interaction contract from the MCP
server with get_component_behavior("RadioGroup") and build the behaviour yourself.
Import
import { RadioGroup, Radio } from '@blakeui/react';Usage
import {Description, Label, Radio, RadioGroup} from "@blakeui/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 '@blakeui/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 "@blakeui/react";
export function CustomIndicator() {Horizontal Orientation
import {Description, Label, Radio, RadioGroup} from "@blakeui/react";
export function Horizontal() {
return (
<div className="flex flex-col gap-4">Controlled
Selected plan: pro
"use client";
import {Description, Label, Radio, RadioGroup} from "@blakeui/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 "@blakeui/react";
import React from "react";
Validation
"use client";
import {Button, Description, FieldError, Form, Label, Radio, RadioGroup} from "@blakeui/react";
import React from "react";
Disabled
import {Description, Label, Radio, RadioGroup} from "@blakeui/react";
export function Disabled() {
return (
<RadioGroup isDisabled defaultValue="pro" name="plan-disabled">Variants
The RadioGroup component supports two visual variants:
primary(default) - Standard styling with default background, suitable for most use casessecondary- Lower emphasis variant, suitable for use in Surface components
Primary variant
Secondary variant
import {Description, Label, Radio, RadioGroup} from "@blakeui/react";
export function Variants() {
return (
<div className="flex flex-col gap-8">In Surface
When used inside a Surface component, use variant="secondary" to apply the lower emphasis variant suitable for surface backgrounds.
import {Description, Label, Radio, RadioGroup, Surface} from "@blakeui/react";
export function OnSurface() {
return (
<Surface className="w-full rounded-3xl p-6">Delivery & Payment
import {Description, Label, Radio, RadioGroup} from "@blakeui/react";
import {Icon} from "@iconify/react";
import clsx from "clsx";
export function DeliveryAndPayment() {Custom Render Function
"use client";
import {Description, Label, Radio, RadioGroup} from "@blakeui/react";
export function CustomRenderFunction() {Styling
Passing Tailwind CSS classes
import { RadioGroup, Radio } from '@blakeui/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.
A worked example is in Styling.
blakeUI 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 |
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 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<keyof React.JSX.IntrinsicElements, RadioRenderProps> | - | Overrides the default DOM element with a custom render function. |
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 |



