A radio button component for single selection from a group of mutually exclusive options
npm install @choice-ui/radioA form control component that allows users to select exactly one option from a set, with multiple visual variants and comprehensive group functionality.
``tsx`
import { Radio, RadioGroup } from "@choice-ui/react"
- Multiple visual variants (default, accent, outline)
- Support for disabled and focused states
- Two label approaches: simple string children or explicit Radio.Label for complex contentRadioGroup
- Controlled usage for reliable state management
- Group functionality via component with two usage patterns
- Proper keyboard navigation (arrow keys within groups)
- Screen reader accessibility with proper ARIA attributes
- Individual and group-level disabled options
`tsx
import { useState } from "react"
const [selected, setSelected] = useState(false)
onChange={setSelected}
>
Option Label
`
`tsx
const [selections, setSelections] = useState({
default: false,
accent: false,
outline: false,
})
<>
onChange={(value) => setSelections({ ...selections, default: value })}
>
Default
onChange={(value) => setSelections({ ...selections, accent: value })}
variant="accent"
>
Accent
onChange={(value) => setSelections({ ...selections, outline: value })}
variant="outline"
>
Outline
>
`
`tsx`
<>
onChange={() => {}}
>
Rest
onChange={() => {}}
focused
>
Focused
onChange={() => {}}
disabled
>
Disabled
>
`tsx
const [simple, setSimple] = useState(false)
const [complex, setComplex] = useState(false)
<>
{/ Simple string label (auto-wrapped) /}
Simple text label
{/ Explicit Radio.Label for complex content /}
Complex label with{" "}
formatting
>
`
`tsx
const [selected, setSelected] = useState("option1")
const options = [
{ value: "option1", label: "First Option" },
{ value: "option2", label: "Second Option" },
{ value: "option3", label: "Third Option" },
]
value={selected}
onChange={setSelected}
/>
`
`tsx
const [selected, setSelected] = useState("choice1")
onChange={setSelected}
>
First Choice
Second Choice
Third Choice
`
`tsx
const [variant, setVariant] = useState("default")
value={variant}
onChange={setVariant}
>
`
`tsx
// Using options prop
const optionsWithDisabled = [
{ value: "available1", label: "Available Option", disabled: false },
{ value: "disabled1", label: "Disabled Option", disabled: true },
{ value: "available2", label: "Another Available", disabled: false },
]
const [selected1, setSelected1] = useState("available1")
value={selected1}
onChange={setSelected1}
/>
// Using RadioGroup.Item with individual disabled control
const [selected2, setSelected2] = useState("custom1")
onChange={setSelected2}
>
`
`ts
interface RadioProps extends Omit
/* Radio content - string is auto-wrapped with Radio.Label /
children?: ReactNode
/* Additional CSS class names /
className?: string
/* Whether the radio appears focused (for keyboard navigation) /
focused?: boolean
/* Callback fired when radio value changes /
onChange: (value: boolean) => void
/* Current radio value /
value: boolean
/* Visual style variant /
variant?: "default" | "accent" | "outline"
}
`
`ts
interface RadioGroupProps {
/* Additional CSS class names /
className?: string
/* Radio group content (RadioGroup.Item components) /
children?: ReactNode
/* Array of option objects (alternative to children) /
options?: Array<{
value: string
label: string
disabled?: boolean
}>
/* Callback fired when selection changes /
onChange: (value: string) => void
/* Current selected value /
value: string
/* Visual variant applied to all radios in the group /
variant?: "default" | "accent" | "outline"
}
interface RadioGroupItemProps {
/* Radio content /
children: ReactNode
/* Whether this option is disabled /
disabled?: boolean
/* Option value /
value: string
}
`
- Defaults:
- variant: "default"focused
- : falsedisabled
- : false (inherited from native prop)
- Accessibility:
- Uses proper role="radio" and aria-checked attributes
- Keyboard navigation with arrow keys in RadioGroups
- Proper focus management and visible focus states
- Label association for screen readers
- Group semantics with fieldset/legend pattern in RadioGroup
- Components use Tailwind CSS via tailwind-variants in shared checkbox/tv.ts.className
- Customize using the prop; classes are merged with internal classes.type: "radio"
- Radio uses the checkbox TV with for rounded appearance.
- Variants affect fill color, border, and focus states.
- Use RadioGroup for related options that require exactly one selection
- Provide clear, concise labels for each option
- Always show all available options (unlike dropdowns)
- Use appropriate variants based on visual hierarchy and importance
- For simple labels, use string children; for complex content, use Radio.Label
- Consider disabled options when choices are conditionally unavailable
- Ensure at least one option remains selectable in groups
- Provide feedback about why options might be disabled
`tsx
const [theme, setTheme] = useState("light")
const [notifications, setNotifications] = useState("email")
$3
`tsx
const [paymentMethod, setPaymentMethod] = useState("card")
`$3
`tsx
const [answer, setAnswer] = useState("")
What is the capital of France?
value={answer}
onChange={setAnswer}
options={[
{ value: "london", label: "London" },
{ value: "berlin", label: "Berlin" },
{ value: "paris", label: "Paris" },
{ value: "madrid", label: "Madrid" },
]}
/>
{answer && (
Selected: {answer}
)}
`Notes
- Radio buttons enforce single selection within a group (use Checkbox for multiple selections)
- String children are automatically wrapped with
Radio.Label for consistent styling
- RadioGroup manages state and provides proper keyboard navigation between options
- Use the options prop for simple cases, RadioGroup.Item` children for complex layouts