A set of radio buttons where only one option can be selected.
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"export function Example() { return ( <RadioGroup defaultValue="comfortable"> <label className="flex items-center gap-2.5 text-sm tracking-wide"> <RadioGroupItem value="default" id="r-basic-default" /> Default </label> <label className="flex items-center gap-2.5 text-sm tracking-wide"> <RadioGroupItem value="comfortable" id="r-basic-comfortable" /> Comfortable </label> <label className="flex items-center gap-2.5 text-sm tracking-wide"> <RadioGroupItem value="compact" id="r-basic-compact" /> Compact </label> </RadioGroup> )}Installation
npx @arctis-sh/@arctis-sh/ui@latest add radio-groupUsage
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"<RadioGroup defaultValue="option-one"> <label className="flex items-center gap-2.5 text-sm tracking-wide"> <RadioGroupItem value="option-one" id="option-one" /> Option A </label> <label className="flex items-center gap-2.5 text-sm tracking-wide"> <RadioGroupItem value="option-two" id="option-two" /> Option B </label></RadioGroup>Composition
RadioGroup ├── RadioGroupItem └── RadioGroupItem
Basic
Simple labeled options. Use defaultValue to set the initial uncontrolled value.
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"export function Example() { return ( <RadioGroup defaultValue="comfortable"> <label className="flex items-center gap-2.5 text-sm tracking-wide"> <RadioGroupItem value="default" id="r-basic-default" /> Default </label> <label className="flex items-center gap-2.5 text-sm tracking-wide"> <RadioGroupItem value="comfortable" id="r-basic-comfortable" /> Comfortable </label> <label className="flex items-center gap-2.5 text-sm tracking-wide"> <RadioGroupItem value="compact" id="r-basic-compact" /> Compact </label> </RadioGroup> )}Description
Add a title and muted helper text when a short label is not enough.
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"export function Example() { return ( <RadioGroup defaultValue="comfortable"> <label className="flex items-start gap-2.5"> <RadioGroupItem value="default" id="r-desc-default" className="mt-0.5" /> <span className="grid gap-1"> <span className="text-sm font-medium tracking-wide">Default</span> <span className="text-sm text-muted-foreground"> Standard spacing for most use cases. </span> </span> </label> <label className="flex items-start gap-2.5"> <RadioGroupItem value="comfortable" id="r-desc-comfortable" className="mt-0.5" /> <span className="grid gap-1"> <span className="text-sm font-medium tracking-wide"> Comfortable </span> <span className="text-sm text-muted-foreground"> More space between elements. </span> </span> </label> <label className="flex items-start gap-2.5"> <RadioGroupItem value="compact" id="r-desc-compact" className="mt-0.5" /> <span className="grid gap-1"> <span className="text-sm font-medium tracking-wide">Compact</span> <span className="text-sm text-muted-foreground"> Minimal spacing for dense layouts. </span> </span> </label> </RadioGroup> )}Card
Wrap each option in a Card for a larger choice surface. Control selection with value and onValueChange; the selected card uses a filled surface.
import * as React from "react"import { Card, CardContent } from "@/components/ui/card"import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"import { cn } from "@/lib/utils"export function Example() { const [plan, setPlan] = React.useState("pro") return ( <RadioGroup value={plan} onValueChange={setPlan} className="w-full gap-3"> <label className="block w-full cursor-pointer"> <Card className={cn("py-0", plan !== "plus" && "bg-transparent")}> <CardContent className="flex items-start gap-2.5 px-2.5 py-2.5"> <RadioGroupItem value="plus" id="r-card-plus" className="mt-0.5" /> <span className="grid gap-1"> <span className="text-sm font-medium tracking-wide">Plus</span> <span className="text-sm text-muted-foreground"> For individuals and small teams. </span> </span> </CardContent> </Card> </label> <label className="block w-full cursor-pointer"> <Card className={cn("py-0", plan !== "pro" && "bg-transparent")}> <CardContent className="flex items-start gap-2.5 px-2.5 py-2.5"> <RadioGroupItem value="pro" id="r-card-pro" className="mt-0.5" /> <span className="grid gap-1"> <span className="text-sm font-medium tracking-wide">Pro</span> <span className="text-sm text-muted-foreground"> For growing businesses. </span> </span> </CardContent> </Card> </label> <label className="block w-full cursor-pointer"> <Card className={cn("py-0", plan !== "enterprise" && "bg-transparent")} > <CardContent className="flex items-start gap-2.5 px-2.5 py-2.5"> <RadioGroupItem value="enterprise" id="r-card-enterprise" className="mt-0.5" /> <span className="grid gap-1"> <span className="text-sm font-medium tracking-wide"> Enterprise </span> <span className="text-sm text-muted-foreground"> For large teams and enterprises. </span> </span> </CardContent> </Card> </label> </RadioGroup> )}Fieldset
Group radios in a native fieldset and legend with helper text.
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"export function Example() { return ( <fieldset className="m-0 grid w-full gap-3 border-0 p-0"> <legend className="float-left w-full p-0"> <span className="grid gap-1 pb-3"> <span className="text-sm font-medium tracking-wide text-foreground"> Subscription Plan </span> <span className="text-sm font-normal text-muted-foreground"> Yearly and lifetime plans offer significant savings. </span> </span> </legend> <RadioGroup defaultValue="yearly"> <label className="flex items-center gap-2.5 text-sm tracking-wide"> <RadioGroupItem value="monthly" id="r-fieldset-monthly" /> Monthly ($9.99/month) </label> <label className="flex items-center gap-2.5 text-sm tracking-wide"> <RadioGroupItem value="yearly" id="r-fieldset-yearly" /> Yearly ($99.99/year) </label> <label className="flex items-center gap-2.5 text-sm tracking-wide"> <RadioGroupItem value="lifetime" id="r-fieldset-lifetime" /> Lifetime ($299.99) </label> </RadioGroup> </fieldset> )}Disabled
disabled on RadioGroup locks every item. Dim the labels so the list reads as inactive.
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"export function Example() { return ( <RadioGroup defaultValue="option-one" disabled> <label className="flex items-center gap-2.5 text-sm tracking-wide opacity-60"> <RadioGroupItem value="option-one" id="r-disabled-one" /> Option One </label> <label className="flex items-center gap-2.5 text-sm tracking-wide opacity-60"> <RadioGroupItem value="option-two" id="r-disabled-two" /> Option Two </label> <label className="flex items-center gap-2.5 text-sm tracking-wide opacity-60"> <RadioGroupItem value="option-three" id="r-disabled-three" /> Option Three </label> </RadioGroup> )}Invalid
Pass aria-invalid to the items for error styling, and use destructive text for their labels.
Notification preferences
Choose how you want to receive notifications.
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"export function Example() { return ( <div className="grid w-full gap-3"> <div className="grid gap-1"> <p className="text-sm font-medium tracking-wide text-destructive"> Notification preferences </p> <p className="text-sm text-muted-foreground"> Choose how you want to receive notifications. </p> </div> <RadioGroup defaultValue="email"> <label className="flex items-center gap-2.5 text-sm tracking-wide text-destructive"> <RadioGroupItem value="email" id="r-invalid-email" aria-invalid /> Email only </label> <label className="flex items-center gap-2.5 text-sm tracking-wide text-destructive"> <RadioGroupItem value="sms" id="r-invalid-sms" aria-invalid /> SMS only </label> <label className="flex items-center gap-2.5 text-sm tracking-wide text-destructive"> <RadioGroupItem value="both" id="r-invalid-both" aria-invalid /> Both Email & SMS </label> </RadioGroup> </div> )}API Reference
RadioGroup
| Prop | Type | Default |
|---|---|---|
| value | string | — |
| defaultValue | string | "" |
| onValueChange | (value: string) => void | — |
| disabled | boolean | — |
| orientation | "vertical" | "horizontal" | "vertical" |
| className | string | — |
<RadioGroup defaultValue="a"> <RadioGroupItem value="a" /> <RadioGroupItem value="b" /></RadioGroup>RadioGroupItem
Must be used inside RadioGroup. Arrow keys move selection within the group. Pass aria-invalid for validation styling.
| Prop | Type | Default |
|---|---|---|
| value | string | — |
| disabled | boolean | — |
| className | string | — |
<RadioGroupItem value="a" />