Github

Radio Group

A set of checkable buttons—known as radio buttons—where no more than one of the buttons can be checked at a time.

import { Label } from "@/components/ui/label";import {	RadioGroup,	RadioGroupItem,} from "@/components/ui/radio-group";
export function RadioGroupDemo() {	return (		<RadioGroup defaultValue="comfortable" className="w-fit">			<div className="flex items-center gap-3">				<RadioGroupItem value="default" id="radio-demo-r1" />				<Label htmlFor="radio-demo-r1">Default</Label>			</div>			<div className="flex items-center gap-3">				<RadioGroupItem value="comfortable" id="radio-demo-r2" />				<Label htmlFor="radio-demo-r2">Comfortable</Label>			</div>			<div className="flex items-center gap-3">				<RadioGroupItem value="compact" id="radio-demo-r3" />				<Label htmlFor="radio-demo-r3">Compact</Label>			</div>		</RadioGroup>	);}

Installation

pnpm dlx shadcn@latest add @herocn/radio-group

Usage

import { Label } from "@/components/ui/label"import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
<RadioGroup defaultValue="option-one">  <div className="flex items-center gap-3">    <RadioGroupItem value="option-one" id="option-one" />    <Label htmlFor="option-one">Option One</Label>  </div>  <div className="flex items-center gap-3">    <RadioGroupItem value="option-two" id="option-two" />    <Label htmlFor="option-two">Option Two</Label>  </div></RadioGroup>

Composition

Use the following composition to build a RadioGroup:

RadioGroup├── RadioGroupItem└── RadioGroupItem

Pair options with Label or Field (FieldLabel, FieldDescription) for accessible names and richer layouts.

Value

Use defaultValue for an uncontrolled group, or value and onValueChange for a controlled selection.

"use client"
import * as React from "react"import { Field, FieldLabel } from "@/components/ui/field"import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
export function Example() {  const [plan, setPlan] = React.useState("monthly")
  return (    <RadioGroup value={plan} onValueChange={setPlan} className="w-fit">      <Field orientation="horizontal">        <RadioGroupItem value="monthly" id="plan-monthly-ex" />        <FieldLabel htmlFor="plan-monthly-ex" className="font-normal">          Monthly        </FieldLabel>      </Field>      <Field orientation="horizontal">        <RadioGroupItem value="yearly" id="plan-yearly-ex" />        <FieldLabel htmlFor="plan-yearly-ex" className="font-normal">          Yearly        </FieldLabel>      </Field>    </RadioGroup>  )}

Examples

Variants

Use the variant prop on RadioGroup for a softer secondary style on tinted panels (see In surface).

import { Field, FieldGroup, FieldLabel } from "@/components/ui/field";import {	RadioGroup,	RadioGroupItem,} from "@/components/ui/radio-group";
export function RadioGroupVariants() {	return (		<FieldGroup className="mx-auto w-full max-w-xs gap-4">			<RadioGroup defaultValue="one" className="w-fit">				<Field orientation="horizontal">					<RadioGroupItem value="one" id="radio-var-def-1" />					<FieldLabel htmlFor="radio-var-def-1" className="font-normal">						Default					</FieldLabel>				</Field>				<Field orientation="horizontal">					<RadioGroupItem value="two" id="radio-var-def-2" />					<FieldLabel htmlFor="radio-var-def-2" className="font-normal">						Style					</FieldLabel>				</Field>			</RadioGroup>			<RadioGroup defaultValue="one" variant="secondary" className="w-fit">				<Field orientation="horizontal">					<RadioGroupItem value="one" id="radio-var-sec-1" />					<FieldLabel htmlFor="radio-var-sec-1" className="font-normal">						Secondary					</FieldLabel>				</Field>				<Field orientation="horizontal">					<RadioGroupItem value="two" id="radio-var-sec-2" />					<FieldLabel htmlFor="radio-var-sec-2" className="font-normal">						Style					</FieldLabel>				</Field>			</RadioGroup>		</FieldGroup>	);}

Description

Use Field, FieldContent, and FieldDescription for helper text beside each option.

Standard spacing for most use cases.

More space between elements.

Minimal spacing for dense layouts.

import {	Field,	FieldContent,	FieldDescription,	FieldLabel,} from "@/components/ui/field";import {	RadioGroup,	RadioGroupItem,} from "@/components/ui/radio-group";
export function RadioGroupDescription() {	return (		<RadioGroup defaultValue="comfortable" className="w-fit">			<Field orientation="horizontal">				<RadioGroupItem value="default" id="radio-desc-r1" />				<FieldContent>					<FieldLabel htmlFor="radio-desc-r1">Default</FieldLabel>					<FieldDescription>						Standard spacing for most use cases.					</FieldDescription>				</FieldContent>			</Field>			<Field orientation="horizontal">				<RadioGroupItem value="comfortable" id="radio-desc-r2" />				<FieldContent>					<FieldLabel htmlFor="radio-desc-r2">Comfortable</FieldLabel>					<FieldDescription>More space between elements.</FieldDescription>				</FieldContent>			</Field>			<Field orientation="horizontal">				<RadioGroupItem value="compact" id="radio-desc-r3" />				<FieldContent>					<FieldLabel htmlFor="radio-desc-r3">Compact</FieldLabel>					<FieldDescription>						Minimal spacing for dense layouts.					</FieldDescription>				</FieldContent>			</Field>		</RadioGroup>	);}

Choice card

Use FieldLabel to wrap the entire Field for a clickable card-style selection.

Compute Environment

Select the compute environment for your cluster.

import {	Field,	FieldContent,	FieldDescription,	FieldGroup,	FieldLabel,	FieldLegend,	FieldSet,	FieldTitle,} from "@/components/ui/field";import {	RadioGroup,	RadioGroupItem,} from "@/components/ui/radio-group";
export function FieldChoiceCard() {	return (		<FieldGroup className="w-full max-w-xs">			<FieldSet>				<FieldLegend variant="label">Compute Environment</FieldLegend>				<FieldDescription>					Select the compute environment for your cluster.				</FieldDescription>				<RadioGroup variant="secondary" defaultValue="kubernetes">					<FieldLabel htmlFor="kubernetes-r2h">						<Field orientation="horizontal">							<FieldContent>								<FieldTitle>Kubernetes</FieldTitle>								<FieldDescription>									Run GPU workloads on a K8s cluster.								</FieldDescription>							</FieldContent>							<RadioGroupItem value="kubernetes" id="kubernetes-r2h" />						</Field>					</FieldLabel>					<FieldLabel htmlFor="vm-z4k">						<Field orientation="horizontal">							<FieldContent>								<FieldTitle>Virtual Machine</FieldTitle>								<FieldDescription>									Access a cluster to run GPU workloads.								</FieldDescription>							</FieldContent>							<RadioGroupItem value="vm" id="vm-z4k" />						</Field>					</FieldLabel>				</RadioGroup>			</FieldSet>		</FieldGroup>	);}

Fieldset

Use FieldSet and FieldLegend to group radio items with a label and description.

Subscription Plan

Yearly and lifetime plans offer significant savings.

import {	Field,	FieldDescription,	FieldLabel,	FieldLegend,	FieldSet,} from "@/components/ui/field";import {	RadioGroup,	RadioGroupItem,} from "@/components/ui/radio-group";
export function FieldRadio() {	return (		<FieldSet className="w-full max-w-xs">			<FieldLegend variant="label">Subscription Plan</FieldLegend>			<FieldDescription>				Yearly and lifetime plans offer significant savings.			</FieldDescription>			<RadioGroup defaultValue="monthly">				<Field orientation="horizontal">					<RadioGroupItem value="monthly" id="plan-monthly" />					<FieldLabel htmlFor="plan-monthly" className="font-normal">						Monthly ($9.99/month)					</FieldLabel>				</Field>				<Field orientation="horizontal">					<RadioGroupItem value="yearly" id="plan-yearly" />					<FieldLabel htmlFor="plan-yearly" className="font-normal">						Yearly ($99.99/year)					</FieldLabel>				</Field>				<Field orientation="horizontal">					<RadioGroupItem value="lifetime" id="plan-lifetime" />					<FieldLabel htmlFor="plan-lifetime" className="font-normal">						Lifetime ($299.99)					</FieldLabel>				</Field>			</RadioGroup>		</FieldSet>	);}

Disabled

Pass disabled to a RadioGroupItem. Add data-disabled on the surrounding Field when the row should use disabled field styling.

import { Field, FieldLabel } from "@/components/ui/field";import {	RadioGroup,	RadioGroupItem,} from "@/components/ui/radio-group";
export function RadioGroupDisabled() {	return (		<RadioGroup defaultValue="option2" className="w-fit">			<Field orientation="horizontal" data-disabled>				<RadioGroupItem value="option1" id="radio-dis-1" disabled />				<FieldLabel htmlFor="radio-dis-1" className="font-normal">					Disabled				</FieldLabel>			</Field>			<Field orientation="horizontal">				<RadioGroupItem value="option2" id="radio-dis-2" />				<FieldLabel htmlFor="radio-dis-2" className="font-normal">					Option 2				</FieldLabel>			</Field>			<Field orientation="horizontal">				<RadioGroupItem value="option3" id="radio-dis-3" />				<FieldLabel htmlFor="radio-dis-3" className="font-normal">					Option 3				</FieldLabel>			</Field>		</RadioGroup>	);}

Invalid

Set aria-invalid on RadioGroupItem and data-invalid on the Field wrapper to show invalid styles.

Notification Preferences

Choose how you want to receive notifications.

import {	Field,	FieldDescription,	FieldLabel,	FieldLegend,	FieldSet,} from "@/components/ui/field";import {	RadioGroup,	RadioGroupItem,} from "@/components/ui/radio-group";
export function RadioGroupInvalid() {	return (		<FieldSet className="w-full max-w-xs">			<FieldLegend variant="label">Notification Preferences</FieldLegend>			<FieldDescription>				Choose how you want to receive notifications.			</FieldDescription>			<RadioGroup defaultValue="email">				<Field orientation="horizontal" data-invalid>					<RadioGroupItem value="email" id="radio-inv-email" aria-invalid />					<FieldLabel htmlFor="radio-inv-email" className="font-normal">						Email only					</FieldLabel>				</Field>				<Field orientation="horizontal" data-invalid>					<RadioGroupItem value="sms" id="radio-inv-sms" aria-invalid />					<FieldLabel htmlFor="radio-inv-sms" className="font-normal">						SMS only					</FieldLabel>				</Field>				<Field orientation="horizontal" data-invalid>					<RadioGroupItem value="both" id="radio-inv-both" aria-invalid />					<FieldLabel htmlFor="radio-inv-both" className="font-normal">						Both Email & SMS					</FieldLabel>				</Field>			</RadioGroup>		</FieldSet>	);}

In surface

Use variant="secondary" when placing a radio group inside a Surface or card so controls match the panel background.

Standard spacing for most use cases.

More space between elements.

Minimal spacing for dense layouts.

import {	Field,	FieldContent,	FieldDescription,	FieldLabel,} from "@/components/ui/field";import {	RadioGroup,	RadioGroupItem,} from "@/components/ui/radio-group";import { Surface } from "@/components/ui/surface";
export function RadioGroupInSurface() {	return (		<Surface className="flex w-full max-w-sm flex-col gap-4 rounded-3xl p-6">			<RadioGroup				defaultValue="comfortable"				variant="secondary"				className="w-fit"			>				<Field orientation="horizontal">					<RadioGroupItem value="default" id="radio-surf-r1" />					<FieldContent>						<FieldLabel htmlFor="radio-surf-r1">Default</FieldLabel>						<FieldDescription>							Standard spacing for most use cases.						</FieldDescription>					</FieldContent>				</Field>				<Field orientation="horizontal">					<RadioGroupItem value="comfortable" id="radio-surf-r2" />					<FieldContent>						<FieldLabel htmlFor="radio-surf-r2">Comfortable</FieldLabel>						<FieldDescription>More space between elements.</FieldDescription>					</FieldContent>				</Field>				<Field orientation="horizontal">					<RadioGroupItem value="compact" id="radio-surf-r3" />					<FieldContent>						<FieldLabel htmlFor="radio-surf-r3">Compact</FieldLabel>						<FieldDescription>							Minimal spacing for dense layouts.						</FieldDescription>					</FieldContent>				</Field>			</RadioGroup>		</Surface>	);}

RTL

Radio layout follows inline direction; use the same Field patterns as in LTR. See the RTL guide for app-wide configuration.

تباعد قياسي لمعظم حالات الاستخدام.

مساحة أكبر بين العناصر.

تباعد أدنى للتخطيطات الكثيفة.

"use client";
import {	type Translations,	useTranslation,} from "@/components/language-selector";import {	Field,	FieldContent,	FieldDescription,	FieldGroup,	FieldLabel,} from "@/components/ui/field";import {	RadioGroup,	RadioGroupItem,} from "@/components/ui/radio-group";
const translations: Translations = {	en: {		dir: "ltr",		values: {			default: "Default",			defaultDescription: "Standard spacing for most use cases.",			comfortable: "Comfortable",			comfortableDescription: "More space between elements.",			compact: "Compact",			compactDescription: "Minimal spacing for dense layouts.",		},	},	ar: {		dir: "rtl",		values: {			default: "افتراضي",			defaultDescription: "تباعد قياسي لمعظم حالات الاستخدام.",			comfortable: "مريح",			comfortableDescription: "مساحة أكبر بين العناصر.",			compact: "مضغوط",			compactDescription: "تباعد أدنى للتخطيطات الكثيفة.",		},	},	he: {		dir: "rtl",		values: {			default: "ברירת מחדל",			defaultDescription: "ריווח סטנדרטי לרוב מקרי השימוש.",			comfortable: "נוח",			comfortableDescription: "יותר מקום בין האלמנטים.",			compact: "קומפקטי",			compactDescription: "ריווח מינימלי לפריסות צפופות.",		},	},};
export function RadioGroupRtl() {	const { language, dir, t } = useTranslation(translations, "ar");
	return (		<FieldGroup lang={language} dir={dir} className="w-full">			<RadioGroup defaultValue="comfortable" className="w-fit">				<Field orientation="horizontal">					<RadioGroupItem value="default" id="radio-rtl-r1" />					<FieldContent>						<FieldLabel htmlFor="radio-rtl-r1">{t.default}</FieldLabel>						<FieldDescription>{t.defaultDescription}</FieldDescription>					</FieldContent>				</Field>				<Field orientation="horizontal">					<RadioGroupItem value="comfortable" id="radio-rtl-r2" />					<FieldContent>						<FieldLabel htmlFor="radio-rtl-r2">{t.comfortable}</FieldLabel>						<FieldDescription>{t.comfortableDescription}</FieldDescription>					</FieldContent>				</Field>				<Field orientation="horizontal">					<RadioGroupItem value="compact" id="radio-rtl-r3" />					<FieldContent>						<FieldLabel htmlFor="radio-rtl-r3">{t.compact}</FieldLabel>						<FieldDescription>{t.compactDescription}</FieldDescription>					</FieldContent>				</Field>			</RadioGroup>		</FieldGroup>	);}

API Reference

RadioGroup

The RadioGroup component forwards props to @base-ui/react RadioGroup and supports the following additional prop:

PropTypeDefault

RadioGroupItem

RadioGroupItem forwards props to the Base UI Radio root. For the full prop surface, see the Base UI Radio Group API.