Radio

Single-pick selection: stacked, inline, card-style.
One job, a platform's own control: iOS has no radio button, so there a single choice is a checkmark list, the inline picker of a grouped form. Each option's label leads and the chosen option carries a trailing check, and a RadioGroup's options form one inset-grouped section with a hairline between two rows. The web and Android keep the ring and dot, and the role is radio on every platform. On iOS the list stays vertical when the group asks for a row (a list has no row form), and card options stay tiles that mark the choice with the same trailing check.
Pass ref to access the interactive radio row, preserving group navigation. Use useRef<ComponentRef<typeof Radio>>(null) from React, or useRef<View>(null) with React Native's View type. Object and callback refs are supported and detach on unmount. Calling ref.current?.focus() or .blur() delegates to the host without activating the control. Browser focus is supported; native focus depends on the platform and React Native version, and is separate from accessibility focus.
On the web, Space activates the focused control on key release, and Enter also activates it. Holding Space does not repeat the change. Moving focus away, disabling the control, or composing text cancels a pending Space press. RadioGroup arrow keys continue to move focus and selection.
iOS
Android
Web
tsx
<RadioGroup defaultValue="pro">
<Radio value="hobby">Hobby</Radio>
<Radio value="pro">Pro</Radio>
<Radio value="enterprise">Enterprise</Radio>
</RadioGroup>

Props

RadioGroup

Prop
Description
value
string | number
The selected option's value (CONTROLLED). Omit for uncontrolled use.
defaultValue
string | number
Initial selected value for uncontrolled use (a bare group selects on press).
onChange
(value: string | number) => void
Fired with the newly selected option's value.
disabled
boolean
Disable every radio in the group.
row
boolean
Lay the options out in a wrapping row instead of the default column. iOS keeps its checkmark list vertical (a list has no row form); `card` options still sit in a row there.
label
string
The group's name, rendered as a heading above the options. It also becomes the radiogroup's accessible name, so a screen reader announces what the set of options is about before reading them. The group owns this label anatomy; never hand-compose a heading Text beside a bare group.
description
ReactNode
Optional muted supporting line rendered under the label.
children
ReactNode
The `<Radio value="…">` options that make up the group.
testID
string
E2E hook forwarded to the group container.
style
StyleProp<ViewStyle>
Outer layout composition only (width/flex within a parent), never a restyle hook.

Radio

Prop
Description
value
string | number
This option's value within a RadioGroup. When the radio sits inside a `<RadioGroup>`, its checked state and selection are driven by the group matching this value; ignored for a standalone radio.
checked
boolean
Whether this control is the selected option (controlled; standalone use).
selected
boolean
Alias for `checked`, for callers that think in terms of "selected".
onChange
(checked: boolean, event: GestureResponderEvent) => void
Fired on press with the next checked value (always true for a radio). Web keyboard activation supports Space on release and Enter; RadioGroup also handles arrows.
testID
string
E2E hook forwarded to the pressable row.
children
ReactNode
Label text (the option's title) shown beside the control.
description
ReactNode
Optional muted secondary line rendered under the label, for the common title-plus-description option (a plan picker, a settings choice). Supplying it builds the stacked title/description layout inside the control, so the caller never hand-composes a Row + Column + two Typography nodes per option.
card
boolean
Render the whole control as a selectable option CARD: the mark (the ring, or the trailing check on iOS) plus the title and description sit inside Card chrome (bordered, padded), and the ENTIRE card is the tap target. When this radio is the chosen option, the card takes Card's own `selected` treatment (a primary border and a soft primary tint), so the whole tile reads as chosen, not just the mark. Pairs with `description` for a plan/tier picker, and is still group-wired through `value` inside a `<RadioGroup>`.
small
boolean
Size (pick one; default is the 16px control).
large
boolean
Size (pick one; default is the 16px control).
disabled
boolean
Dim the control and block presses.
style
LayoutStyle
Composition within a parent only, never a restyle hook and never a width: the parent layout container provides the bounds.
ref
React.Ref<View>
Ref to the interactive radio row, preserving group navigation. Typed as a React Native View. On web, React Native Web exposes its DOM host; focus() and blur() move browser focus. Native host behavior depends on the platform and React Native version. Calling focus() does not activate the control or call accessibility focus APIs.

Do & Don’t

Default selection

Don’t
Plan
Leaving a radio group with nothing selected forces an extra decision and can submit empty.
Do
Plan
Pre-select a sensible default so the common path needs no clicks, and name the set with the group's own label.

Stacked

Don’t
Pro
For growing teams that need more control.
Center-aligning the control floats it to the vertical middle of a two-line label, leaving it visually unattached to the title it controls.
Do
Pass a description and the control stacks the title over its secondary line for you, the mark aligned to the title (the ring to its first line on the web and Android, the check centered on the row on iOS).

Inline

Don’t
Cramped spacing between options makes each label blur into the next radio, so it is hard to tell which dot belongs to which choice.
Do
Keep generous spacing between options and tighter spacing inside each so every label clearly pairs with its own control.

Card

Don’t
Pro
For growing teams.
Enterprise
Advanced security.
When the selected card keeps the same plain border, only the tiny native dot signals the choice and the active card is easy to miss.
Do
Give the selected card a primary border and tinted fill so the whole tile reads as chosen, not just the dot. Pass card and the control derives that treatment from its checked state automatically.