Skip to main content

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

PropTypeDefaultDescription
optionsRadioOption[]List of radio options
valuestringControlled selected value
defaultValuestringInitial value for uncontrolled usage
onChange(value: string) => voidCalled when selection changes
namestringNative radio name for form submission
disabledbooleanfalseDisables the entire group
classNamestring""Additional class names

RadioOption

FieldTypeDescription
labelReactNodeVisible label content
valuestringUnique option value
disabledbooleanDisables this option

Behavior Notes

  • Automatically switches between controlled and uncontrolled modes.
  • Arrow keys move selection between enabled options.
  • Home selects the first enabled option.
  • End selects 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 option
    • ArrowDown / ArrowRight → next option
    • Home → first enabled option
    • End → last enabled option
  • Disabled state announced via aria-disabled.


Layout

  • Displays as a vertical flex column by default.
  • Controlled by .ui-radio-group styles.
  • 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.