vera logoVera UI
ComponentsForm Components

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-ui
npm install @helgadigitals/vera-ui
yarn add @helgadigitals/vera-ui

Usage

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

Preferred contact method *

Newsletter frequency

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

AttributeValuesDescription
data-orientation"horizontal" | "vertical"The orientation of the group

RadioGroupItem

AttributeValuesDescription
data-state"checked" | "unchecked"The checked state of the radio button
data-disabledPresent when disabledApplied 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>