radio.tsx
Overview
This file implements a customizable and accessible Radio Button component for React applications, including support for radio button groups. It provides two main components:
Radio: A single radio button that can be controlled individually or as part of a group.Radio.Group: A group container component that manages the state of multiple radio buttons, allowing only one selection at a time.
The components support controlled and uncontrolled modes, disabled state, and flexible layout directions (horizontal or vertical). The file also uses React Context to share state and behavior between the group and individual radios, and integrates iconography from the lucide-react library for checked indicators.
Components and Types
1. RadioGroupContext
Type:
React.ContextPurpose: Provides the current selected value, a change handler, and disabled state from the radio group to individual radio buttons.
Context Value Shape:
{ value: string | number; onChange: (value: string | number) => void; disabled?: boolean; } | null
2. Radio
Description
A single radio button component that renders a clickable label with a custom styled radio indicator, supporting both controlled and uncontrolled usage. It integrates with RadioGroupContext to synchronize selection state when used within a group.
Props
Prop | Type | Description |
|---|---|---|
| `string \ | number` |
|
| If provided, controls checked state externally (controlled). |
|
| Disables the radio button when true. |
|
| Callback triggered when the checked state changes. |
|
| Label content rendered next to the radio button. |
Behavior
If
checkedprop is provided, the component acts as controlled; otherwise, it uses context to determine checked state.Disabled state merges local
disabledprop and group-level disabled state.Clicking the radio:
Does nothing if disabled.
Calls
onChangecallback with the toggled checked state.If part of a group, calls the group's
onChangewith the radio'svalue.
Usage Example
<Radio value="option1" onChange={(checked) => console.log('Checked:', checked)}>
Option 1
</Radio>
When used inside a group:
<Radio.Group value={selectedValue} onChange={setSelectedValue}>
<Radio value="option1">Option 1</Radio>
<Radio value="option2">Option 2</Radio>
</Radio.Group>
3. Radio.Group
Description
A container component that manages a group of radio buttons, controlling their selection state and disabling behavior. It uses React Context to provide group state and handlers to its child radios.
Props
Prop | Type | Description |
|---|---|---|
| `string \ | number` (optional) |
| `string \ | number` (optional) |
| `(value: string \ | number) => void` (optional) |
|
| Disables all radios in the group when true. |
|
| The |
|
| Additional CSS classes for the container element. |
| `'horizontal' \ | 'vertical' |
Behavior
Supports controlled (
valueprop) and uncontrolled (defaultValue+ internal state) modes.Calls
onChangecallback on selection changes.Passes down disabled state to all children radios, merging with their own disabled props.
Renders children with a flex container, adjusting layout direction accordingly.
Usage Example
<Radio.Group defaultValue="option1" onChange={(val) => console.log(val)} direction="vertical">
<Radio value="option1">Option 1</Radio>
<Radio value="option2">Option 2</Radio>
<Radio value="option3" disabled>Option 3 (disabled)</Radio>
</Radio.Group>
Implementation Details
Controlled vs Uncontrolled
Radio and Radio.Group support controlled and uncontrolled usage.
Controlled components rely on props (
checkedforRadio,valueforGroup) to determine state.Uncontrolled components manage internal state (
useState) and update it on interactions.
Context API
RadioGroupContextprovides a communication mechanism between the group and radios.Radios subscribe to this context to align their checked state and disable state with the group.
This pattern avoids prop drilling and enables flexible composition.
Styling and Accessibility
Uses utility function
cn(classNames) to conditionally merge CSS classes.The radio indicator is a styled
<span>with a border and background that changes when checked.Uses
LucideRadioicon to visually represent the checked state.The label uses appropriate cursor styles and opacity when disabled.
Focus-visible styles are applied for keyboard accessibility.
Commented Out Code
There is commented-out internal state management inside
Radioto support uncontrolled mode at the radio level, but currently, the component relies on group context or controlled props.
Interactions with Other Parts of the System
Uses
cnfunction imported from@/lib/utilsfor class name concatenation.Uses
LucideRadioicon fromlucide-reactfor the checked indicator.Designed to be a reusable UI component in a larger React app.
Exports a single object
Radiowhich hasGroupas a static property, enabling usage like<Radio.Group>.
Component Structure Diagram
componentDiagram
component Radio {
+value: string | number
+checked?: boolean
+disabled?: boolean
+onChange?: (checked: boolean) => void
+children?: ReactNode
+handleClick()
}
component RadioGroup {
+value?: string | number
+defaultValue?: string | number
+onChange?: (value: string | number) => void
+disabled?: boolean
+children: ReactNode
+className?: string
+direction?: 'horizontal' | 'vertical'
}
component RadioGroupContext {
+value: string | number
+onChange: (value: string | number) => void
+disabled?: boolean
}
RadioGroupContext <.. Radio : uses
RadioGroup o-- RadioGroupContext : provides context
RadioGroup "1" *-- "*" Radio : contains
Summary
The radio.tsx file provides a flexible, accessible, and composable radio button system for React, with support for controlled/uncontrolled usage and grouped selection management. It leverages React Context to coordinate shared state and behavior between the group and individual radios, ensuring consistent UX and easy integration in larger forms or UI modules. The components are styled with utility classes and include keyboard accessibility considerations.
Exported API
import { Radio } from 'radio.tsx';
// Usage:
<Radio value="1" checked onChange={...}>Option 1</Radio>
<Radio.Group value={selected} onChange={setSelected} direction="vertical" disabled={false}>
<Radio value="1">Option 1</Radio>
<Radio value="2">Option 2</Radio>
</Radio.Group>