Core Component
Radio
Radio group for mutually exclusive selection with spring-animated selection dot and context-based state sharing.
Preview
Claude 3.5 Sonnet
Best for complex reasoning
GPT-4o
Fast multimodal model
Gemini Pro
Google's flagship model
Source
Full component implementation using the design system tokens.
"use client";
import { createContext, useContext, useState } from "react";
type RadioContextType = { value: string; onChange: (value: string) => void; name: string };
const RadioContext = createContext<RadioContextType | null>(null);
export function DSRadioGroup({
value: controlledValue, defaultValue = "", onChange, name, children,
}: {
value?: string; defaultValue?: string; onChange?: (value: string) => void;
name: string; children: React.ReactNode;
}) {
const [internalValue, setInternalValue] = useState(defaultValue);
const isControlled = controlledValue !== undefined;
const currentValue = isControlled ? controlledValue : internalValue;
const handleChange = (val: string) => {
if (!isControlled) setInternalValue(val);
onChange?.(val);
};
return (
<RadioContext.Provider value={{ value: currentValue, onChange: handleChange, name }}>
<div role="radiogroup" className="flex flex-col gap-3">{children}</div>
</RadioContext.Provider>
);
}
export function DSRadioItem({
value, label, description, disabled = false,
}: {
value: string; label: string; description?: string; disabled?: boolean;
}) {
const ctx = useContext(RadioContext);
if (!ctx) throw new Error("DSRadioItem must be used within DSRadioGroup");
const selected = ctx.value === value;
const select = () => { if (!disabled) ctx.onChange(value); };
return (
<div className="flex items-start gap-3">
<button role="radio" aria-checked={selected} onClick={select} disabled={disabled}
className={`relative inline-flex items-center justify-center w-5 h-5 shrink-0 rounded-full border-2 transition-all duration-150 active:scale-[0.95] mt-0.5 ${selected ? "border-accent" : "border-overlay/15 hover:border-overlay/25"} ${disabled ? "opacity-50 cursor-not-allowed active:scale-100" : ""}`}
style={{ transitionTimingFunction: "var(--ease-out)" }}>
<span className="w-2.5 h-2.5 rounded-full bg-accent" style={{
opacity: selected ? 1 : 0,
transform: selected ? "scale(1)" : "scale(0)",
transition: "opacity 150ms var(--ease-out), transform 200ms var(--ease-out)",
}} />
</button>
{(label || description) && (
<div className={disabled ? "opacity-50" : "cursor-pointer"} onClick={disabled ? undefined : select}>
{label && <p className="text-sm font-medium text-foreground">{label}</p>}
{description && <p className="text-xs text-tertiary mt-0.5">{description}</p>}
</div>
)}
</div>
);
}Props
All available props with types and defaults.
| Prop | Type | Default | Description |
|---|---|---|---|
name* | string | — | Group name for form association (RadioGroup) |
label* | string | — | Label text (RadioItem) |
value | string | — | Controlled selected value (RadioGroup) |
defaultValue | string | '' | Initial value (RadioGroup, uncontrolled) |
onChange | (value: string) => void | — | Called when selection changes (RadioGroup) |
description | string | — | Helper text (RadioItem) |
disabled | boolean | false | Disables the radio item |
Variants
Default Group
Basic radio group with three options.
Claude 3.5 Sonnet
GPT-4o
Gemini Pro
<DSRadioGroup name="model" defaultValue="claude">
<DSRadioItem value="claude" label="Claude 3.5 Sonnet" />
<DSRadioItem value="gpt4" label="GPT-4o" />
<DSRadioItem value="gemini" label="Gemini Pro" />
</DSRadioGroup>With Descriptions
Radio items with helper text for context.
Precise
Lower temperature, more focused
Balanced
Default settings
Creative
Higher temperature, more varied
<DSRadioGroup name="mode" defaultValue="balanced">
<DSRadioItem value="precise" label="Precise" description="Lower temperature, more focused" />
<DSRadioItem value="balanced" label="Balanced" description="Default settings" />
<DSRadioItem value="creative" label="Creative" description="Higher temperature, more varied" />
</DSRadioGroup>With Disabled Item
Some options can be individually disabled.
Free
Pro
Enterprise
<DSRadioGroup name="tier">
<DSRadioItem value="free" label="Free" />
<DSRadioItem value="pro" label="Pro" />
<DSRadioItem value="enterprise" label="Enterprise" disabled />
</DSRadioGroup>Prompt Guide
Prompt Guide — Radio
Use for
- Model selection (mutually exclusive)
- Response mode (precise / balanced / creative)
- Export format selection
- Tier or plan selection
Don't use for
- Multi-select — use Checkbox group
- Binary toggle — use Toggle or Checkbox
- More than 7 options — use Select dropdown
AI Context
Radio uses React context to share group state. The selection dot animates from scale(0) to scale(1) with 200ms ease-out for a spring-like feel. Use for 3-7 mutually exclusive options. For more, switch to Select.