Components
- Accordion
- Alert
- Alert Dialog
- Aspect Ratio
- Attachment
- Avatar
- Badge
- Breadcrumb
- Bubble
- Button
- Button Group
- Calendar
- Card
- Carousel
- Chart
- Checkbox
- Collapsible
- Combobox
- Command
- Context Menu
- Date Picker
- Dialog
- Drawer
- Dropdown Menu
- Empty
- Field
- Hover Card
- Input
- Input OTP
- Input Group
- Item
- Kbd
- Label
- Marker
- Menubar
- Message
- Message Scroller
- Native Select
- Navigation Menu
- Pagination
- Popover
- Progress
- Radio Group
- Resizable
- Scroll Area
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Sonner
- Spinner
- Switch
- Table
- Tabs
- Textarea
- Toggle
- Toggle Group
- Tooltip
- New Component
Elements
Charts
Icons
import { Label } from "@/components/ui/label";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
export const RadioGroupDemo = () => (
<div className="flex flex-col gap-8">
{/* Vertical (default) */}
<RadioGroup defaultValue="comfortable">
<div className="flex items-center gap-2">
<RadioGroupItem value="default" id="r-default" />
<Label htmlFor="r-default">Default</Label>
</div>
<div className="flex items-center gap-2">
<RadioGroupItem value="comfortable" id="r-comfortable" />
<Label htmlFor="r-comfortable">Comfortable</Label>
</div>
<div className="flex items-center gap-2">
<RadioGroupItem value="compact" id="r-compact" />
<Label htmlFor="r-compact">Compact</Label>
</div>
<div className="flex items-center gap-2">
<RadioGroupItem value="disabled" id="r-disabled" disabled />
<Label htmlFor="r-disabled">Disabled option</Label>
</div>
</RadioGroup>
{/* Horizontal */}
<RadioGroup defaultValue="card" className="flex gap-6">
<div className="flex items-center gap-2">
<RadioGroupItem value="card" id="r-card" />
<Label htmlFor="r-card">Card</Label>
</div>
<div className="flex items-center gap-2">
<RadioGroupItem value="paypal" id="r-paypal" />
<Label htmlFor="r-paypal">PayPal</Label>
</div>
<div className="flex items-center gap-2">
<RadioGroupItem value="apple" id="r-apple" />
<Label htmlFor="r-apple">Apple Pay</Label>
</div>
</RadioGroup>
</div>
);With label
Pair each RadioGroupItem with a <Label> using matching id / htmlFor. Clicking the label selects the item.
<RadioGroup defaultValue="comfortable">
<div className="flex items-center gap-2">
<RadioGroupItem value="default" id="r1" />
<Label htmlFor="r1">Default</Label>
</div>
<div className="flex items-center gap-2">
<RadioGroupItem value="comfortable" id="r2" />
<Label htmlFor="r2">Comfortable</Label>
</div>
</RadioGroup>Horizontal
Override the default grid gap-3 with a flex row.
<RadioGroup defaultValue="card" className="flex gap-6">
<div className="flex items-center gap-2">
<RadioGroupItem value="card" id="pay-card" />
<Label htmlFor="pay-card">Card</Label>
</div>
<div className="flex items-center gap-2">
<RadioGroupItem value="paypal" id="pay-paypal" />
<Label htmlFor="pay-paypal">PayPal</Label>
</div>
</RadioGroup>Disabled item
Disable a single option with the disabled prop on RadioGroupItem.
<RadioGroupItem value="enterprise" id="r-enterprise" disabled />
<Label htmlFor="r-enterprise">Enterprise (coming soon)</Label>Controlled
const [value, setValue] = useState("comfortable");
<RadioGroup value={value} onValueChange={setValue}>
...
</RadioGroup>With Field
import { Field, FieldError, FieldLabel } from "@/components/ui/field";
<Field error="Please select a plan.">
<FieldLabel>Plan</FieldLabel>
<RadioGroup defaultValue="">
<div className="flex items-center gap-2">
<RadioGroupItem value="free" id="plan-free" />
<Label htmlFor="plan-free">Free</Label>
</div>
<div className="flex items-center gap-2">
<RadioGroupItem value="pro" id="plan-pro" />
<Label htmlFor="plan-pro">Pro</Label>
</div>
</RadioGroup>
<FieldError />
</Field>Props
RadioGroup
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Controlled selected value |
defaultValue | string | — | Uncontrolled initial value |
onValueChange | (value: string) => void | — | Callback when selection changes |
disabled | boolean | false | Disables all items |
required | boolean | false | Marks the group as required |
className | string | — | Default layout is grid gap-3 |
RadioGroupItem
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | The value this item represents (required) |
disabled | boolean | false | Disables this specific item |
aria-invalid | boolean | — | Applies destructive ring |
Usage
import { Label } from "@/components/ui/label";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
<RadioGroup defaultValue="option-1">
<div className="flex items-center gap-2">
<RadioGroupItem value="option-1" id="opt-1" />
<Label htmlFor="opt-1">Option 1</Label>
</div>
<div className="flex items-center gap-2">
<RadioGroupItem value="option-2" id="opt-2" />
<Label htmlFor="opt-2">Option 2</Label>
</div>
</RadioGroup>Specs
# Mint — Radio Group Specs
## Component
- name: radio-group
- file: registry/mint/radio-group.tsx
- installs to: components/ui/radio-group.tsx
- dependencies: radix-ui, lucide-react
- registryDependencies: none
## What is it
A set of mutually exclusive radio buttons built on Radix UI RadioGroup. Use it
whenever a user must pick exactly one option from a short list (2–6 items).
Prefer a Select when the list is long or space is constrained.
## Visual identity
Minimal circular indicator: a 16px rounded-full border ring that collapses
into a filled inner circle (8px) when selected. The soft, translucent
--input border and vivid green --primary fill give it Mint's fresh, on-brand look when selected.
## Design tokens
--input: oklch(0.88 0.04 155 / 60%) / oklch(0.32 0.05 155 / 40%) — item border (resting)
--ring: oklch(0.5 0.14 155) / oklch(0.62 0.15 155) — green focus ring colour
--primary: oklch(0.5 0.14 155) / oklch(0.62 0.15 155) — selected indicator fill and text colour
--destructive: oklch(0.55 0.25 15) / oklch(0.704 0.191 22.216) — error border and ring
## Sub-components
- RadioGroup → Root container; default layout is grid gap-3 (vertical stack)
- RadioGroupItem → Individual radio button; 16px rounded-full with border and shadow-xs
- RadioGroupIndicator → Internal; renders CircleIcon (fill-primary, size-2) when item is checked
## States
- focus-visible: border changes to --ring, 3px ring in --ring/50 colour
- aria-invalid: border changes to --destructive, ring in --destructive/20 (light) / --destructive/40 (dark)
- disabled: cursor-not-allowed, opacity-50; also applies when disabled prop is set on RadioGroup root
- checked: RadioGroupIndicator becomes visible, CircleIcon fills with --primary
## Conventions
- Always pair each RadioGroupItem with a <Label> using matching id / htmlFor — clicking the label selects the item
- Default layout is grid gap-3 (vertical); use className="flex gap-6" on RadioGroup for horizontal layout
- The group navigates with arrow keys natively (Radix); Tab moves focus into/out of the group as a unit
- Wrap in a Field component when validation error messaging is required (Field exposes aria-invalid to children)
- Sub-components expose data-slot="radio-group", data-slot="radio-group-item", and data-slot="radio-group-indicator" for CSS targetingInstallation
$ npx shadcn add \https://gin-design.vercel.app/radio-group