RadioGroup
An accessible WAI-ARIA compliant radio group component for selecting a single option from a list. Supports keyboard navigation, controlled and uncontrolled usage, and disabled states.
Usage
Basic usage (uncontrolled)
import { RadioGroup } from '@nofinite/nui';
<RadioGroup
name="gender"
defaultValue="male"
options={[
{ value: 'male', label: 'Male' },
{ value: 'female', label: 'Female' },
]}
/>;
Controlled usage
const [value, setValue] = useState('1');
<RadioGroup
value={value}
onChange={setValue}
options={[
{ value: '1', label: 'One' },
{ value: '2', label: 'Two' },
{ value: '3', label: 'Three' },
]}
/>;
Variants
RadioGroup does not have visual variants. Styling is handled via CSS variables and class names.
Guidelines:
- Use consistent spacing and grouping for related choices.
- Avoid mixing radio groups with unrelated options.
Sizes
Size is controlled via CSS. The component does not expose a size prop.
To customize size, override styles:
.ui-radio-input {
width: 20px;
height: 20px;
}
States
Disabled (group-level)
<RadioGroup
disabled
options={[
{ value: 'a', label: 'Option A' },
{ value: 'b', label: 'Option B' },
]}
/>
Disabled (option-level)
<RadioGroup
options={[
{ value: '1', label: 'One' },
{ value: '2', label: 'Two', disabled: true },
{ value: '3', label: 'Three' },
]}
/>
State behavior:
- Disabled radios are not focusable or selectable.
- Disabled options are skipped during keyboard navigation.
- Checked state is reflected using
data-state="checked".
Native Props / Composition
The component renders native <input type="radio"> elements and forwards relevant attributes.
<RadioGroup
name="example"
className="my-radio-group"
onChange={(value) => console.log(value)}
options={[
{ value: 'x', label: 'X' },
{ value: 'y', label: 'Y' },
]}
/>
You can extend styling via className:
<RadioGroup className="custom-radio-group" options={...} />
Props
| Prop | Type | Default | Description |
|---|---|---|---|
options | RadioOption[] | — | List of radio options |
value | string | — | Controlled selected value |
defaultValue | string | — | Initial value for uncontrolled usage |
onChange | (value: string) => void | — | Called when selection changes |
name | string | — | Native radio name for form submission |
disabled | boolean | false | Disables the entire group |
className | string | "" | Additional class names |
RadioOption
| Field | Type | Description |
|---|---|---|
label | ReactNode | Visible label content |
value | string | Unique option value |
disabled | boolean | Disables this option |
Behavior Notes
- Automatically switches between controlled and uncontrolled modes.
- Arrow keys move selection between enabled options.
Homeselects the first enabled option.Endselects the last enabled option.- Focus follows selection during keyboard navigation.
// Arrow keys update both focus and checked value
Accessibility
-
Renders a container with
role="radiogroup". -
Uses native
<input type="radio">elements. -
Keyboard support:
ArrowUp/ArrowLeft→ previous optionArrowDown/ArrowRight→ next optionHome→ first enabled optionEnd→ last enabled option
-
Disabled state announced via
aria-disabled.
Layout
- Displays as a vertical flex column by default.
- Controlled by
.ui-radio-groupstyles. - Can be made horizontal via CSS override:
.ui-radio-group {
flex-direction: row;
}
Best Practices
Do
- Use for mutually exclusive choices.
- Provide clear, concise labels.
- Group logically related options.
Don’t
- Use for multi-select scenarios (use checkboxes instead).
- Nest interactive elements inside labels.
- Change selection without user intent.