nori-ui

Radio

Single-selection group with full keyboard navigation and roving tabindex.

3.3 kBgzipped

At a glance

  • <Radio.Group> owns the selected value (controlled via value + onChange, or uncontrolled via defaultValue).
  • <Radio> declares a single option — it must be rendered inside a <Radio.Group> or you'll get a runtime error explaining why.
  • Keyboard nav follows the WAI-ARIA radiogroup pattern: arrow keys move between options (selection follows focus), Home / End jump to first / last, wrap-around at the edges.
  • Roving tabindex: tabbing into the group lands on the selected option; only one option participates in the tab order at a time.

Preview

Direction:
Locale:

<Radio.Group> value and defaultValue

Controlled vs. uncontrolled selection. The Group fires onChange(next) whenever the user moves through the options.

// Controlled
const [plan, setPlan] = useState<'free' | 'pro' | 'team'>('free');
<Radio.Group value={plan} onChange={setPlan} aria-label="Plan">
    <Radio value="free" label="Free" />
    <Radio value="pro" label="Pro" />
    <Radio value="team" label="Team" />
</Radio.Group>
 
// Uncontrolled
<Radio.Group defaultValue="free" aria-label="Plan">{/* … */}</Radio.Group>

<Radio.Group> disabled

Disables every option in the group. Individual options can also opt out with their own disabled.

<Radio.Group disabled value="pro">{/* … */}</Radio.Group>

<Radio> label

Renders a clickable label next to the dot. Pressing the label is the same target as pressing the dot. For richer label content (a price tag, helper text), pass children instead — the children slot replaces the auto-rendered label.

<Radio value="pro" label="Pro — $12 / month" />
<Radio value="team">
    <YourCustomPlanRow />
</Radio>

<Radio> disabled

Greys an individual option out and skips it during arrow-key navigation.

<Radio value="enterprise" label="Enterprise" disabled />

Naming the group

Always name the group via aria-label, aria-labelledby, or a visible heading wrapped in aria-labelledby. An un-named radiogroup is hard for assistive tech to announce as a unit.

<>
    <Text variant="body-sm" id="plan-heading">Choose a plan</Text>
    <Radio.Group aria-labelledby="plan-heading" defaultValue="free">{/* … */}</Radio.Group>
</>

Grouped with Field.Group

For label + description + error around a radio set, use <Field.Group>:

import { useState } from 'react';
import { Field, Radio } from '@nori-ui/core';
 
export const Example = () => {
    const [value, setValue] = useState<string | null>(null);
    return (
        <Field.Group label="Plan" description="Pick the tier that fits your team." required>
            <Radio.Group value={value} onChange={setValue}>
                <Radio value="hobby" label="Hobby" />
                <Radio value="pro" label="Pro" />
                <Radio value="enterprise" label="Enterprise" />
            </Radio.Group>
        </Field.Group>
    );
};

See Field for the full API.

Props

<Radio.Group>

PropTypeDefaultDescription
accessibilityDescribedBystringReact Native equivalent of `aria-describedby`. Injected by Field.Control.
accessibilityLabelledBystringReact Native equivalent of `aria-labelledby`. Injected by Field.Control.
aria-describedbystringPoints to description / error elements. Injected by Field.Control.
aria-invalidbooleanMarks the group as invalid. Injected by Field.Control when Field has an error.
aria-labelledbystringPoints to the element that labels this group. Injected by Field.Control.
aria-requiredbooleanMarks the group as required. Injected by Field.Control when Field is required.
classNamestring
defaultValuestringUncontrolled initial value.
disabledbooleanfalseWhen true, every Radio inside is non-interactive. Individual Radios can also opt in.
idstringHTML id forwarded to the radiogroup container. Injected by Field.Control.
namestringHTML `name` for form integration on web. Optional but recommended.
nativeIDstringReact Native equivalent of `id`. Injected by Field.Control.
onChange(next: string) => voidFires with the new value when the selection changes.
orientationenumverticalLayout orientation. Drives the keyboard nav axis (Down/Up for vertical, Right/Left for horizontal — both pairs work in either orientation as a usability bonus). @defaultValue 'vertical'
testIDstring
valuestringControlled selected value.

<Radio>

PropTypeDefaultDescription
value*stringUnique value within the group. Required.
childrenReactNodeCustom content rendered next to the radio dot. Overrides `label`.
classNamestring
disabledbooleanfalseDisable just this option.
labelstringVisible label that doubles as the accessibility label.
testIDstring

On this page

Preview theme