Radio

stable
Since 1.1
A single composable radio control, for rows that need custom content beyond a flat label and description.
Forms
primitive
Display
Documented
import { Radio } from '@ivim/native/components';

When to use

Use when
A radio row needs custom content — a price, a badge, a trailing control — that an options array cannot express.
You are building a bespoke choice layout and want the control without the group chrome.
Don't use when
The options are a plain list of labels and descriptions — use RadioGroup, which handles exclusivity and the group name for you.
The options should read as cards — use SelectableCardGroup.
More than one option may be chosen — use Checkbox.

Playground

Adjust the controls to preview live prop combinations. The code snippet updates to match the current selection.
size
size
Invalid
isInvalid
Disabled
isDisabled
<Radio />

Example

Composed set
Operable pair (parent owns the value)
Small
Individual states
Selected
Unselected
With description
With a tooltip
Invalid
Read only (still announced as enabled)
Disabled
Custom row content
Label as a custom node

Anatomy

The parts that compose this component.
1
Control
The circle carrying border and fill.
2
Dot
Inner fill shown when selected; animates in.
3
Label
The option text, and the accessible name.
4
Description
Supporting text beneath the label; takes the error color when invalid.

Guidelines

Do
Own the selected value in the parent and pass isSelected to each radio — a lone Radio has no notion of its siblings.
Do
Give every radio in a set the same group context in its accessible name, as RadioGroup does automatically.
Don't
Render a single radio on its own — a choice of one cannot be deselected and should be a checkbox.

Props

Public props for this component. Required props are marked; defaults reflect the component implementation.
value
required
string
Value reported through onChange when this radio is chosen.
isSelected
boolean
default: false
Whether this radio is the chosen one. Owned by the parent.
onChange
(value: string) => void
Fires with this radio’s value on press.
label / description
ReactNode
Option text and supporting text.
tooltip
string
Contextual help behind a small info affordance beside the row. Supplementary only — anything required to act belongs in description.
isDisabled
boolean
default: false
Dims, blocks input, and announces disabled.
isReadOnly
boolean
default: false
Blocks input but still announces as an enabled control.
isInvalid
boolean
default: false
Applies the error treatment.
size
'sm' | 'md'
default: 'md'
Control and label scale.
isTabbable / onKeyDown
boolean / (event) => void
Roving-tabindex position and web key handler. RadioGroup sets both so the group is one tab stop with arrow-key selection; a lone Radio rarely needs them.
aria-label
string
Accessible name when there is no visible label, or when the label is not plain text.

Deviations from Untitled UI

Where this component deliberately differs from Untitled UI's API, and why. These are kept choices, not gaps to close.
Untitled UI
`hint?: ReactNode` doubling as error text when invalid
Ours
`description` + `errorMessage`
UUI's `hint` is a UUI-ism layered over React Aria, which itself uses `description`/`errorMessage`. We follow the substrate; both slots can render simultaneously.

Accessibility

Roles and screen-reader behavior this component provides, plus keyboard interactions.
Announces accessibilityRole="radio" with checked state — the previous implementation set no role at all, so radios announced as plain pressables.
Takes its name from a string label, or from aria-label when the label is not plain text.
Read-only rejects input while still announcing as an enabled control.
Exclusivity is not enforced here: a lone Radio reports only its own state, so the parent or RadioGroup must guarantee one-of-many.
Tab
Moves focus to the radio.
Space
Selects the focused radio.
Arrow keys
Not handled by a lone Radio — RadioGroup owns arrow-key traversal.

Platform & RTL

Behavior that differs across the platforms this component runs on.
Web
Focusable with a visible inset focus ring matched to the circular radius.
iOS
VoiceOver announces the radio trait and its selected state.
Android
TalkBack announces the radio trait and its selected state.
RTL
The control and label swap sides with the writing direction.

State coverage

Interaction and visual states this component supports.
base
semantic color
density

Token references

Semantic design tokens this component page exercises. Tap one to edit it live.