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> );}pnpm dlx shadcn@latest add @herocn/radio-groupimport { 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>Use the following composition to build a RadioGroup:
RadioGroup├── RadioGroupItem└── RadioGroupItemPair options with Label or Field (FieldLabel, FieldDescription) for accessible names and richer layouts.
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> )}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> );}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> );}Use FieldLabel to wrap the entire Field for a clickable card-style selection.
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> );}Use FieldSet and FieldLegend to group radio items with a label and description.
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> );}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> );}Set aria-invalid on RadioGroupItem and data-invalid on the Field wrapper to show invalid styles.
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> );}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> );}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> );}The RadioGroup component forwards props to @base-ui/react RadioGroup and supports the following additional prop:
| Prop | Type | Default |
|---|---|---|
| variant | "default" | "secondary" | "default" |
Secondary removes heavy shadow on items so the group reads cleanly on tinted surfaces. | ||
RadioGroupItem forwards props to the Base UI Radio root. For the full prop surface, see the Base UI Radio Group API.