Radio Group
A radio group component built on Radix UI for mutually exclusive selections with keyboard navigation.
Usage
Basic Radio Group
import { RadioGroup, RadioGroupItem } from "@prisma-docs/eclipse";
export function BasicRadioGroup() {
return (
<RadioGroup defaultValue="option-one">
<div className="flex items-center gap-2">
<RadioGroupItem value="option-one" id="option-one" />
<label htmlFor="option-one" className="text-sm font-medium">Option One</label>
</div>
<div className="flex items-center gap-2">
<RadioGroupItem value="option-two" id="option-two" />
<label htmlFor="option-two" className="text-sm font-medium">Option Two</label>
</div>
</RadioGroup>
);
}Live Example:
With Descriptions
import { RadioGroup, RadioGroupItem } from "@prisma-docs/eclipse";
export function RadioGroupWithDescriptions() {
return (
<RadioGroup defaultValue="comfortable">
<div className="flex items-center gap-2">
<RadioGroupItem value="default" id="default" />
<div className="flex flex-col gap-1">
<label htmlFor="default" className="text-sm font-medium">Default</label>
<span className="text-sm text-foreground-neutral-weak">The standard option for most users.</span>
</div>
</div>
<div className="flex items-center gap-2">
<RadioGroupItem value="comfortable" id="comfortable" />
<div className="flex flex-col gap-1">
<label htmlFor="comfortable" className="text-sm font-medium">Comfortable</label>
<span className="text-sm text-foreground-neutral-weak">More spacing for better readability.</span>
</div>
</div>
<div className="flex items-center gap-2">
<RadioGroupItem value="compact" id="compact" />
<div className="flex flex-col gap-1">
<label htmlFor="compact" className="text-sm font-medium">Compact</label>
<span className="text-sm text-foreground-neutral-weak">Minimal spacing to fit more content.</span>
</div>
</div>
</RadioGroup>
);
}Live Example:
The standard option for most users.
More spacing for better readability.
Minimal spacing to fit more content.
With Field Component
import { RadioGroup, RadioGroupItem, Field, FieldLabel, FieldDescription } from "@prisma-docs/eclipse";
export function RadioGroupWithField() {
return (
<Field>
<FieldLabel>Notification Frequency</FieldLabel>
<FieldDescription>Choose how often you want to receive notifications.</FieldDescription>
<RadioGroup defaultValue="daily">
<div className="flex items-center gap-2">
<RadioGroupItem value="realtime" id="realtime" />
<label htmlFor="realtime" className="text-sm font-medium">Real-time</label>
</div>
<div className="flex items-center gap-2">
<RadioGroupItem value="daily" id="daily" />
<label htmlFor="daily" className="text-sm font-medium">Daily digest</label>
</div>
<div className="flex items-center gap-2">
<RadioGroupItem value="weekly" id="weekly" />
<label htmlFor="weekly" className="text-sm font-medium">Weekly summary</label>
</div>
</RadioGroup>
</Field>
);
}Live Example:
Choose how often you want to receive notifications.
Controlled Radio Group
import { RadioGroup, RadioGroupItem } from "@prisma-docs/eclipse";
import { useState } from "react";
export function ControlledRadioGroup() {
const [value, setValue] = useState("option-one");
return (
<div className="space-y-4">
<RadioGroup value={value} onValueChange={setValue}>
<div className="flex items-center gap-2">
<RadioGroupItem value="option-one" id="controlled-one" />
<label htmlFor="controlled-one" className="text-sm font-medium">Option One</label>
</div>
<div className="flex items-center gap-2">
<RadioGroupItem value="option-two" id="controlled-two" />
<label htmlFor="controlled-two" className="text-sm font-medium">Option Two</label>
</div>
</RadioGroup>
<span className="text-sm text-foreground-neutral-weak">
Selected: {value}
</span>
</div>
);
}Live Example:
Disabled State
import { RadioGroup, RadioGroupItem } from "@prisma-docs/eclipse";
export function DisabledRadioGroup() {
return (
<RadioGroup defaultValue="option-one">
<div className="flex items-center gap-2">
<RadioGroupItem value="option-one" id="disabled-one" disabled />
<label htmlFor="disabled-one" className="text-sm font-medium opacity-70 cursor-not-allowed">Disabled option (selected)</label>
</div>
<div className="flex items-center gap-2">
<RadioGroupItem value="option-two" id="disabled-two" disabled />
<label htmlFor="disabled-two" className="text-sm font-medium opacity-70 cursor-not-allowed">Disabled option</label>
</div>
<div className="flex items-center gap-2">
<RadioGroupItem value="option-three" id="enabled-three" />
<label htmlFor="enabled-three" className="text-sm font-medium">Enabled option</label>
</div>
</RadioGroup>
);
}Live Example:
Horizontal Layout
import { RadioGroup, RadioGroupItem } from "@prisma-docs/eclipse";
export function HorizontalRadioGroup() {
return (
<RadioGroup defaultValue="small" className="flex gap-4">
<div className="flex items-center gap-2">
<RadioGroupItem value="small" id="size-small" />
<label htmlFor="size-small" className="text-sm font-medium">Small</label>
</div>
<div className="flex items-center gap-2">
<RadioGroupItem value="medium" id="size-medium" />
<label htmlFor="size-medium" className="text-sm font-medium">Medium</label>
</div>
<div className="flex items-center gap-2">
<RadioGroupItem value="large" id="size-large" />
<label htmlFor="size-large" className="text-sm font-medium">Large</label>
</div>
</RadioGroup>
);
}Live Example:
In Forms
import { RadioGroup, RadioGroupItem, Button } from "@prisma-docs/eclipse";
export function RadioGroupForm() {
return (
<form className="space-y-6 max-w-md">
<div className="space-y-4">
<h3 className="text-lg font-semibold">Shipping Method</h3>
<RadioGroup defaultValue="standard" name="shipping">
<div className="flex items-center justify-between p-4 border rounded-lg">
<div className="flex items-center gap-3">
<RadioGroupItem value="standard" id="standard-shipping" />
<div className="flex flex-col gap-1">
<label htmlFor="standard-shipping" className="text-sm font-medium">Standard Shipping</label>
<span className="text-sm text-foreground-neutral-weak">5-7 business days</span>
</div>
</div>
<span className="text-sm font-medium">Free</span>
</div>
<div className="flex items-center justify-between p-4 border rounded-lg">
<div className="flex items-center gap-3">
<RadioGroupItem value="express" id="express-shipping" />
<div className="flex flex-col gap-1">
<label htmlFor="express-shipping" className="text-sm font-medium">Express Shipping</label>
<span className="text-sm text-foreground-neutral-weak">2-3 business days</span>
</div>
</div>
<span className="text-sm font-medium">$15.00</span>
</div>
<div className="flex items-center justify-between p-4 border rounded-lg">
<div className="flex items-center gap-3">
<RadioGroupItem value="overnight" id="overnight-shipping" />
<div className="flex flex-col gap-1">
<label htmlFor="overnight-shipping" className="text-sm font-medium">Overnight Shipping</label>
<span className="text-sm text-foreground-neutral-weak">Next business day</span>
</div>
</div>
<span className="text-sm font-medium">$30.00</span>
</div>
</RadioGroup>
</div>
<Button type="submit">Continue to payment</Button>
</form>
);
}Live Example:
Component Props
RadioGroup
value- Controlled value (string, optional)defaultValue- Default value for uncontrolled usage (string, optional)onValueChange- Callback when value changes (function, optional)disabled- Whether the group is disabled (boolean, default: false)required- Whether selection is required (boolean, default: false)name- Name for form submission (string, optional)className- Additional CSS classes (string, optional)- All standard Radix UI RadioGroup Root props
RadioGroupItem
value- Value of this radio item (string, required)disabled- Whether this item is disabled (boolean, default: false)id- HTML id attribute (string, optional)className- Additional CSS classes (string, optional)- All standard Radix UI RadioGroup Item props
Features
- ✅ Built on Radix UI for robust accessibility
- ✅ Keyboard navigation (Arrow keys, Tab, Space)
- ✅ Controlled and uncontrolled modes
- ✅ Disabled state for individual items or entire group
- ✅ Focus visible indicators
- ✅ Selected state with animated indicator
- ✅ Works with forms and validation
- ✅ Screen reader support
- ✅ Flexible layout (vertical or horizontal)
- ✅ Customizable with className
- ✅ Fully typed with TypeScript
Best Practices
- Always use labels with radio buttons for better UX
- Use
htmlForon labels to match RadioGroupItemid - Group related options together logically
- Provide descriptions for complex choices
- Use controlled state when you need to react to changes
- Set a sensible
defaultValuefor uncontrolled usage - Consider using Field components for forms
- Test keyboard navigation thoroughly
- Ensure labels are clickable
- Use disabled state sparingly
- Provide enough spacing between options
Common Use Cases
The RadioGroup component is perfect for:
- Settings selection - Choose one option from several
- Shipping methods - Delivery options
- Payment methods - Credit card, PayPal, etc.
- Plan selection - Subscription tiers
- Preferences - Theme, language, etc.
- Surveys - Single-choice questions
- Filters - Sorting and filtering options
- Size selection - Product sizes (S, M, L, XL)
Accessibility
The RadioGroup component follows accessibility best practices:
- Uses Radix UI's accessible radio group primitive
- Proper ARIA attributes (
role="radiogroup",aria-checked) - Keyboard accessible (Arrow keys to navigate, Space to select)
- Focus visible indicators
- Screen reader support with proper labels
- Disabled state prevents interaction
- Works with form validation
- Supports required attribute
- Proper focus management within group
- High contrast indicators
Keyboard Shortcuts
- Arrow Up/Down - Navigate between radio items (vertical)
- Arrow Left/Right - Navigate between radio items (horizontal)
- Space - Select focused radio item
- Tab - Move focus to next element outside group
- Shift + Tab - Move focus to previous element
Styling
The RadioGroup component uses design tokens:
- Border:
border-primary - Selected indicator:
fill-primary - Focus ring:
ring-ring - Size: 16×16px (h-4 w-4)
- Indicator size: 14×14px (h-3.5 w-3.5)
- Border radius:
rounded-full - Gap:
gap-2(8px between items)
Customize by passing className:
<RadioGroup className="flex gap-4">
<RadioGroupItem className="border-2 border-blue-500" />
</RadioGroup>Integration with Other Components
RadioGroup works well with:
- Field - Form structure with labels and descriptions
- FieldLabel - Group labels
- FieldDescription - Helper text for the group
- FieldError - Validation messages
- Forms - Form submission and validation
- Cards - Options within cards
- Dialogs - Settings in modals
Form Integration
The RadioGroup component works seamlessly with forms:
<form onSubmit={handleSubmit}>
<RadioGroup name="plan" defaultValue="pro" required>
<RadioGroupItem value="free" id="free" />
<RadioGroupItem value="pro" id="pro" />
</RadioGroup>
<button type="submit">Submit</button>
</form>When selected, the form data will include the selected value with the specified name.