Radio Group
A set of mutually exclusive choices, built on top of Radix UI Radio Group primitive for keyboard navigation and screen reader support.
radioGroup.tsx
Core implementation of the RadioGroup component.
ReactRadix UITailwindForms
"use client";
import * as React from "react";
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
import { Circle } from "lucide-react";
import { cn } from "@/lib/utils";
export interface RadioGroupProps
extends React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root> {
color?: "default" | "primary" | "secondary" | "accent" | "success" | "warning" | "danger";
}
const colorMap = {
default: "text-foreground border-input data-[state=checked]:border-foreground",
primary: "text-primary border-input data-[state=checked]:border-primary",
secondary: "text-secondary border-input data-[state=checked]:border-secondary",
accent: "text-accent border-input data-[state=checked]:border-accent",
success: "text-success border-input data-[state=checked]:border-success",
warning: "text-warning border-input data-[state=checked]:border-warning",
danger: "text-danger border-input data-[state=checked]:border-danger",
};
const RadioGroupContext = React.createContext<{ color?: RadioGroupProps["color"] }>({});
const RadioGroup = React.forwardRef<
React.ComponentRef<typeof RadioGroupPrimitive.Root>,
RadioGroupProps
>(({ className, color = "primary", ...props }, ref) => {
return (
<RadioGroupContext.Provider value={{ color }}>
<RadioGroupPrimitive.Root
className={cn("grid gap-2.5", className)}
{...props}
ref={ref}
/>
</RadioGroupContext.Provider>
);
});
RadioGroup.displayName = RadioGroupPrimitive.Root.displayName;
export interface RadioGroupItemProps
extends React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item> {
label?: React.ReactNode;
description?: React.ReactNode;
}
const RadioGroupItem = React.forwardRef<
React.ComponentRef<typeof RadioGroupPrimitive.Item>,
RadioGroupItemProps
>(({ className, label, description, id, disabled, ...props }, ref) => {
const generatedId = React.useId();
const itemId = id || generatedId;
const { color = "primary" } = React.useContext(RadioGroupContext);
return (
<div className="inline-flex items-start gap-2.5">
<RadioGroupPrimitive.Item
ref={ref}
id={itemId}
disabled={disabled}
className={cn(
"aspect-square size-4 rounded-full border transition-all duration-200 outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 cursor-pointer mt-0.5 flex items-center justify-center",
colorMap[color],
className
)}
{...props}
>
<RadioGroupPrimitive.Indicator className="flex items-center justify-center">
<Circle className="size-2 fill-current text-current" />
</RadioGroupPrimitive.Indicator>
</RadioGroupPrimitive.Item>
{(label || description) && (
<div className="flex flex-col gap-0.5 select-none">
{label && (
<label
htmlFor={itemId}
className="text-sm font-medium leading-none cursor-pointer peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
{label}
</label>
)}
{description && (
<p className="text-xs text-muted-foreground">{description}</p>
)}
</div>
)}
</div>
);
});
RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;
export { RadioGroup, RadioGroupItem };Basic Usage
Standard radio group selection.
Standard compact layout
Spacious padding and larger line height
Dense data density
Colors
Radio group theme colors.
color:
default | primary | secondary | accent | success | warning | dangerAPI Reference
Props — RadioGroup
Properties to configure the RadioGroup component.
| Prop | Type | Default | Description |
|---|---|---|---|
| color | 'default' | 'primary' | 'secondary' | 'accent' | 'success' | 'warning' | 'danger' | 'primary' | Color theme applied to active radio items. |
| defaultValue | string | — | Initial selected item value. |