Radio

Atom
Single-pick selection: stacked, inline, card-style.

Playground

Every control sets a real prop of RadioProps. The JSX below updates as you go.
Props
value
one of
card
boolean
Size
pick one
disabled
boolean
JSX · RadioProps
<Radio value="stable" />

Variants

3 examples from src/atoms/radio/radio.md, each rendered live from its own JSX.
Inline
row lays the options out in a wrapping row. The group fills its parent's width (the parent provides the bounds, as for any list or field), so the row wraps onto another line when the options run out of room instead of running past its container.
<RadioGroup row defaultValue="hobby">
<Radio value="hobby">Hobby</Radio>
<Radio value="pro">Pro</Radio>
<Radio value="enterprise">Enterprise</Radio>
</RadioGroup>
Card
<RadioGroup row defaultValue="pro">
<Radio card value="hobby" description="For personal projects.">Hobby</Radio>
<Radio card value="pro" description="For growing teams.">Pro</Radio>
<Radio card value="enterprise" description="Advanced security and support.">Enterprise</Radio>
</RadioGroup>
With description
<RadioGroup defaultValue="pro">
<Radio value="hobby" description="For personal projects and experiments.">Hobby</Radio>
<Radio value="pro" description="For growing teams that need more control.">Pro</Radio>
<Radio value="enterprise" description="Advanced security, compliance, and support.">Enterprise</Radio>
</RadioGroup>

Sizes

small
default
large

States

Hover, focus and press are pinned so they can be compared side by side; the rest are real props.
default
hover
focus
disabled

Anatomy

1
Ring
2
Dot
3
Label
4
Description
Dimmed parts appear only in some states or variants.

Props

15 props on RadioProps, grouped as they are declared in source. Required props are marked *.
Prop
Type
Default
Description
Props
11 props
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
false
Whether this control is the selected option (controlled; standalone use).
selected
boolean
false
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
false
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>.
meta
ReactNode
–
A trailing detail on the title's line (a version, a price, a status pill): a string is set in the small mono meta type in the subtle ink, a node (a Badge) as it is. The title takes the row, so in a full-width option the detail sits at its end; in a row group's hugging option it follows the title.
icon
ReactNode
–
A card tile's icon (a kit Icon with no tone of its own): the tile stacks the icon and the mark on one line above the title and description. The icon takes the accent text ink while the option is chosen and the subtle ink otherwise. Ignored without card.
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.
Size
pick one
4 props
small
boolean
false
large
boolean
false
disabled
boolean
false
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.

Accessibility and keyboard

Keyboard
Tab
Enters the group on the checked option
ArrowDown
ArrowRight
Checks the next option
ArrowUp
ArrowLeft
Checks the previous option
Space
Checks the focused option
Semantics
Roles
radio
States
checked
disabled
ARIA on web
aria-checked
aria-hidden
aria-label
Below the platform minimum, the touch area grows to 44 pt on iOS and 48 dp on Android without moving the layout.
From the docs
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.

Platform skins

The playground's props on each platform: same tree, native shapes, type and touch sizes.
Web
control 10 · field 10 · card 16 · Instrument Sans
Digest
iOS
capsule controls · field 8 · card 12 · SF Pro · 44 pt
Android
Material 3 stadium · field 4 · card 12 · Roboto · 48 dp

Usage

<RadioGroup defaultValue="pro">
<Radio value="hobby">Hobby</Radio>
<Radio value="pro">Pro</Radio>
<Radio value="enterprise">Enterprise</Radio>
</RadioGroup>
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 the role is radio on every platform: on the web an 18 px ring (16 small, 22 large) whose edge thickens in the accent around a hollow center when chosen, on Android Material 3's 20dp ring around an accent dot. In glass mode the ring keeps its solid look on every platform: a ring is a small mark inside content, not a surface, so it draws with no pane and no rim. A card option is a content-layer pane, as a selectable Card is. 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.

Tokens

Read from the playground render. Values follow the scheme and the ion hue.
--fd
--ft
--ion
--ln2
--tx3

Related components