Radio Group
A radio group is a set of checkable buttons where only one can be checked at a time. Built on Radix UI's RadioGroup primitive, it provides accessible single-choice selection with keyboard navigation.
Installation
pnpm add @helgadigitals/vera-uinpm install @helgadigitals/vera-uiyarn add @helgadigitals/vera-uiUsage
import { RadioGroup, RadioGroupItem, Label } from "@helgadigitals/vera-ui"export default function Example() {return ( <RadioGroup defaultValue="option1"> <div className="flex items-center space-x-2"> <RadioGroupItem value="option1" id="r1" /> <Label htmlFor="r1">Option 1</Label> </div> <div className="flex items-center space-x-2"> <RadioGroupItem value="option2" id="r2" /> <Label htmlFor="r2">Option 2</Label> </div> </RadioGroup>)}Examples
Basic Radio Group
Choose your preferred size
import { RadioGroup, RadioGroupItem, Label } from "@helgadigitals/vera-ui"export default function BasicExample() {return ( <div className="space-y-3"> <h3 className="text-sm font-medium">Choose your preferred size</h3> <RadioGroup defaultValue="medium"> <div className="flex items-center space-x-2"> <RadioGroupItem value="small" id="size-small" /> <Label htmlFor="size-small">Small</Label> </div> <div className="flex items-center space-x-2"> <RadioGroupItem value="medium" id="size-medium" /> <Label htmlFor="size-medium">Medium</Label> </div> <div className="flex items-center space-x-2"> <RadioGroupItem value="large" id="size-large" /> <Label htmlFor="size-large">Large</Label> </div> </RadioGroup> </div>)}Controlled Radio Group
Select a plan
Selected: pro
import { useState } from "react"import { RadioGroup, RadioGroupItem, Label } from "@helgadigitals/vera-ui"export default function ControlledExample() {const [selectedPlan, setSelectedPlan] = useState("pro")return ( <div className="space-y-3"> <h3 className="text-sm font-medium">Select a plan</h3> <RadioGroup value={selectedPlan} onValueChange={setSelectedPlan}> <div className="flex items-center space-x-2"> <RadioGroupItem value="free" id="plan-free" /> <Label htmlFor="plan-free">Free - $0/month</Label> </div> <div className="flex items-center space-x-2"> <RadioGroupItem value="pro" id="plan-pro" /> <Label htmlFor="plan-pro">Pro - $10/month</Label> </div> <div className="flex items-center space-x-2"> <RadioGroupItem value="enterprise" id="plan-enterprise" /> <Label htmlFor="plan-enterprise">Enterprise - $50/month</Label> </div> </RadioGroup> <p className="text-sm text-muted-foreground"> Selected: {selectedPlan} </p> </div>)}Horizontal Layout
Payment method
import { RadioGroup, RadioGroupItem, Label } from "@helgadigitals/vera-ui"export default function HorizontalExample() {return ( <div className="space-y-3"> <h3 className="text-sm font-medium">Payment method</h3> <RadioGroup defaultValue="card" className="flex space-x-6"> <div className="flex items-center space-x-2"> <RadioGroupItem value="card" id="payment-card" /> <Label htmlFor="payment-card">Credit Card</Label> </div> <div className="flex items-center space-x-2"> <RadioGroupItem value="paypal" id="payment-paypal" /> <Label htmlFor="payment-paypal">PayPal</Label> </div> <div className="flex items-center space-x-2"> <RadioGroupItem value="bank" id="payment-bank" /> <Label htmlFor="payment-bank">Bank Transfer</Label> </div> </RadioGroup> </div>)}With Descriptions
Choose delivery option
5-7 business days • Free
2-3 business days • $9.99
Next business day • $24.99
import { RadioGroup, RadioGroupItem, Label } from "@helgadigitals/vera-ui"export default function WithDescriptionsExample() {return ( <div className="space-y-3"> <h3 className="text-sm font-medium">Choose delivery option</h3> <RadioGroup defaultValue="standard" className="space-y-4"> <div className="flex items-start space-x-3"> <RadioGroupItem value="standard" id="delivery-standard" className="mt-1" /> <div className="space-y-1"> <Label htmlFor="delivery-standard">Standard Delivery</Label> <p className="text-sm text-muted-foreground"> 5-7 business days • Free </p> </div> </div> <div className="flex items-start space-x-3"> <RadioGroupItem value="express" id="delivery-express" className="mt-1" /> <div className="space-y-1"> <Label htmlFor="delivery-express">Express Delivery</Label> <p className="text-sm text-muted-foreground"> 2-3 business days • $9.99 </p> </div> </div> <div className="flex items-start space-x-3"> <RadioGroupItem value="overnight" id="delivery-overnight" className="mt-1" /> <div className="space-y-1"> <Label htmlFor="delivery-overnight">Overnight Delivery</Label> <p className="text-sm text-muted-foreground"> Next business day • $24.99 </p> </div> </div> </RadioGroup> </div>)}Disabled State
Disabled Group
Mixed State
import { RadioGroup, RadioGroupItem, Label } from "@helgadigitals/vera-ui"export default function DisabledExample() {return ( <div className="space-y-6"> {/* Disabled group */} <div className="space-y-3"> <h3 className="text-sm font-medium">Disabled Group</h3> <RadioGroup defaultValue="option1" disabled> <div className="flex items-center space-x-2"> <RadioGroupItem value="option1" id="disabled-1" /> <Label htmlFor="disabled-1">Option 1</Label> </div> <div className="flex items-center space-x-2"> <RadioGroupItem value="option2" id="disabled-2" /> <Label htmlFor="disabled-2">Option 2</Label> </div> </RadioGroup> </div> {/* Individual disabled items */} <div className="space-y-3"> <h3 className="text-sm font-medium">Mixed State</h3> <RadioGroup defaultValue="available1"> <div className="flex items-center space-x-2"> <RadioGroupItem value="available1" id="mixed-1" /> <Label htmlFor="mixed-1">Available Option 1</Label> </div> <div className="flex items-center space-x-2"> <RadioGroupItem value="disabled" id="mixed-2" disabled /> <Label htmlFor="mixed-2">Disabled Option</Label> </div> <div className="flex items-center space-x-2"> <RadioGroupItem value="available2" id="mixed-3" /> <Label htmlFor="mixed-3">Available Option 2</Label> </div> </RadioGroup> </div> </div>)}Form Integration
Current Values:
{
"contactMethod": "email",
"frequency": "never"
}import { useForm, Controller } from "react-hook-form"import { RadioGroup, RadioGroupItem, Label, Button } from "@helgadigitals/vera-ui"export default function FormExample() {const { control, handleSubmit, formState: { errors } } = useForm()const onSubmit = (data) => { console.log(data)}return ( <form onSubmit={handleSubmit(onSubmit)} className="space-y-6"> <div className="space-y-3"> <h3 className="text-sm font-medium">Preferred contact method *</h3> <Controller name="contactMethod" control={control} rules={{ required: "Please select a contact method" }} render={({ field }) => ( <RadioGroup value={field.value || ""} onValueChange={field.onChange} > <div className="flex items-center space-x-2"> <RadioGroupItem value="email" id="contact-email" /> <Label htmlFor="contact-email">Email</Label> </div> <div className="flex items-center space-x-2"> <RadioGroupItem value="phone" id="contact-phone" /> <Label htmlFor="contact-phone">Phone</Label> </div> <div className="flex items-center space-x-2"> <RadioGroupItem value="sms" id="contact-sms" /> <Label htmlFor="contact-sms">SMS</Label> </div> </RadioGroup> )} /> {errors.contactMethod && ( <p className="text-sm text-destructive"> {errors.contactMethod.message} </p> )} </div> <div className="space-y-3"> <h3 className="text-sm font-medium">Newsletter frequency</h3> <Controller name="frequency" control={control} defaultValue="weekly" render={({ field }) => ( <RadioGroup value={field.value} onValueChange={field.onChange} > <div className="flex items-center space-x-2"> <RadioGroupItem value="daily" id="freq-daily" /> <Label htmlFor="freq-daily">Daily</Label> </div> <div className="flex items-center space-x-2"> <RadioGroupItem value="weekly" id="freq-weekly" /> <Label htmlFor="freq-weekly">Weekly</Label> </div> <div className="flex items-center space-x-2"> <RadioGroupItem value="monthly" id="freq-monthly" /> <Label htmlFor="freq-monthly">Monthly</Label> </div> <div className="flex items-center space-x-2"> <RadioGroupItem value="never" id="freq-never" /> <Label htmlFor="freq-never">Never</Label> </div> </RadioGroup> )} /> </div> <Button type="submit">Submit</Button> </form>)}Card-style Options
Choose your plan
Perfect for getting started
- Up to 3 projects
- Community support
Best for professionals
- Unlimited projects
- Priority support
- Advanced features
For large organizations
- Everything in Pro
- Custom integrations
- Dedicated support
Selected Plan: pro
Best for professionals
import { useState } from "react"import { RadioGroup, RadioGroupItem, Label } from "@helgadigitals/vera-ui"import { Check } from "lucide-react"export default function CardStyleExample() {const [selectedTier, setSelectedTier] = useState("pro")return ( <div className="space-y-3"> <h3 className="text-sm font-medium">Choose your plan</h3> <RadioGroup value={selectedTier} onValueChange={setSelectedTier} className="space-y-2" > {[ { value: "free", title: "Free", description: "Perfect for getting started", price: "$0", features: ["Up to 3 projects", "Community support"] }, { value: "pro", title: "Pro", description: "Best for professionals", price: "$10", features: ["Unlimited projects", "Priority support", "Advanced features"] }, { value: "enterprise", title: "Enterprise", description: "For large organizations", price: "$50", features: ["Everything in Pro", "Custom integrations", "Dedicated support"] } ].map((plan) => ( <div key={plan.value} className={`relative flex items-start p-4 border rounded-lg cursor-pointer transition-colors ${ selectedTier === plan.value ? "border-primary bg-primary/5" : "border-input hover:bg-accent" }`} > <RadioGroupItem value={plan.value} id={`plan-${plan.value}`} className="mt-1" /> <div className="ml-3 flex-1"> <Label htmlFor={`plan-${plan.value}`} className="font-medium cursor-pointer"> {plan.title} - {plan.price}/month </Label> <p className="text-sm text-muted-foreground mb-2"> {plan.description} </p> <ul className="text-sm text-muted-foreground space-y-1"> {plan.features.map((feature, index) => ( <li key={index} className="flex items-center gap-2"> <Check className="h-3 w-3 text-primary" /> {feature} </li> ))} </ul> </div> </div> ))} </RadioGroup> </div>)}API Reference
RadioGroup Props
The RadioGroup component accepts all props from Radix UI's RadioGroup component:
Prop
Type
RadioGroupItem Props
Prop
Type
Data Attributes
The following data attributes are automatically applied and can be used for styling:
RadioGroup
| Attribute | Values | Description |
|---|---|---|
data-orientation | "horizontal" | "vertical" | The orientation of the group |
RadioGroupItem
| Attribute | Values | Description |
|---|---|---|
data-state | "checked" | "unchecked" | The checked state of the radio button |
data-disabled | Present when disabled | Applied when the radio button is disabled |
Accessibility
The RadioGroup component follows WAI-ARIA guidelines:
- Keyboard Navigation:
- Arrow keys move between radio buttons
- Space selects the focused radio button
- Tab moves focus to and from the radio group
- Screen Readers: Proper ARIA attributes and semantic radio elements
- Focus Management: Visual focus indicators and proper focus handling
- Grouping: Radio buttons are properly grouped for screen readers
Single Selection: Radio groups enforce single selection - selecting one option automatically deselects others. Use checkboxes if you need multiple selection.
Best Practices
Always Use Labels
Every radio button should have an associated label:
// Good: Properly labeled
<div className="flex items-center space-x-2">
<RadioGroupItem value="option1" id="opt1" />
<Label htmlFor="opt1">Option 1</Label>
</div>
// Avoid: No label
<RadioGroupItem value="option1" />Logical Grouping
Group related options together and provide clear group labels:
// Good: Clear grouping with descriptive heading
<div className="space-y-3">
<h3 className="text-sm font-medium">Shipping Options</h3>
<RadioGroup>
{/* options */}
</RadioGroup>
</div>Default Selection
Consider providing a sensible default selection:
// Good: Reasonable default
<RadioGroup defaultValue="standard">
<div className="flex items-center space-x-2">
<RadioGroupItem value="express" id="express" />
<Label htmlFor="express">Express ($10)</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="standard" id="standard" />
<Label htmlFor="standard">Standard (Free)</Label>
</div>
</RadioGroup>Styling
The RadioGroup components support custom styling and automatically adapt to your theme:
<RadioGroup className="space-y-4">
<RadioGroupItem
className="border-2 border-blue-500 text-blue-500"
// ...props
/>
</RadioGroup>Multi Select
A powerful multi-select component that allows users to choose multiple values from a dropdown list. Features include search functionality, select all/clear options, badge display for selected items, and customizable animations.
Select
A select component built on Radix UI's Select primitive that allows users to choose a single value from a dropdown list of options. Features keyboard navigation, accessibility, and customizable styling.