Components
Radio
Grouped controls for selecting a single value in forms
Migrating from v1
The v2 RadioGroup is built on React Aria and styled with Thumbprint v2 semantic tokens. It replaces the v1 Radio, which required every radio to share a name and manage its own checked state. With v2 you render a single RadioGroup that owns the selection, and one RadioGroup.Radio per option.
Key differences:
- Selection moves to the group. Replace each radio’s
isCheckedandonChange(isChecked, id, event)with the group’svalue/defaultValueandonChange(value). EachRadioGroup.Radiotakes a uniquevalue. - No more shared
namewiring or page-unique values for keyboard navigation — React Aria handles grouping, roving focus, and arrow-key selection. - Per-radio props carry over with the same names:
isDisabled,hasError,radioVerticalAlign,labelPadding,id,accessibilityLabel, anddataTestId.isDisabledandhasErrorcan also be set once on the group and cascade to every radio. - New capabilities: a
type="control"card treatment, adescriptionper radio, and a grouporientation. - The deprecated
dataTestprop is removed (usedataTestId).
Before (v1):
function RadioExample() {const [selectedId, setSelectedId] = React.useState('long-distance');return (<div><Radioid="long-distance"name="moving"isChecked={selectedId === 'long-distance'}onChange={(isChecked, id) => setSelectedId(id)}>Long Distance Moving</Radio><Radioid="furniture"name="moving"isChecked={selectedId === 'furniture'}onChange={(isChecked, id) => setSelectedId(id)}>Furniture Moving</Radio></div>);}
After (v2):
function RadioExample() {const [value, setValue] = React.useState('long-distance');return (<RadioGroup value={value} onChange={setValue} accessibilityLabel="Moving services"><RadioGroup.Radio value="long-distance">Long Distance Moving</RadioGroup.Radio><RadioGroup.Radio value="furniture">Furniture Moving</RadioGroup.Radio></RadioGroup>);}
Basic radio group
Provide value and onChange for a controlled group. Each RadioGroup.Radio reports its value when selected, and only one radio in the group can be selected at a time.
function RadioExample() { const [value, setValue] = React.useState('long-distance'); return ( <RadioGroup value={value} onChange={setValue} accessibilityLabel="Moving services"> <RadioGroup.Radio value="long-distance">Long Distance Moving</RadioGroup.Radio> <RadioGroup.Radio value="furniture">Furniture Moving and Heavy Lifting</RadioGroup.Radio> <RadioGroup.Radio value="pool-table">Pool Table Moving</RadioGroup.Radio> </RadioGroup> ); }
Uncontrolled groups
For an uncontrolled group, set an initial selection with defaultValue and let React Aria manage the state internally.
<RadioGroup defaultValue="long-distance" accessibilityLabel="Moving services"> <RadioGroup.Radio value="long-distance">Long Distance Moving</RadioGroup.Radio> <RadioGroup.Radio value="furniture">Furniture Moving and Heavy Lifting</RadioGroup.Radio> <RadioGroup.Radio value="pool-table">Pool Table Moving</RadioGroup.Radio> </RadioGroup>
Disabled radios
The isDisabled prop visually and functionally disables a radio along with its label. Set it on a single RadioGroup.Radio to disable one option, or on the RadioGroup to disable the whole group.
<RadioGroup defaultValue="long-distance" accessibilityLabel="Moving services"> <RadioGroup.Radio value="long-distance">Long Distance Moving</RadioGroup.Radio> <RadioGroup.Radio value="furniture" isDisabled> Furniture Moving and Heavy Lifting </RadioGroup.Radio> <RadioGroup.Radio value="pool-table">Pool Table Moving</RadioGroup.Radio> </RadioGroup>
Radio group with an error
The hasError prop visually represents an error and marks the group invalid for assistive technologies. Set on the group it cascades to every radio; individual radios can override it. It should be used alongside an error message that helps users advance through the form.
function RadioExample() { const [value, setValue] = React.useState(undefined); return ( <RadioGroup value={value} onChange={setValue} hasError accessibilityLabel="Moving services"> <RadioGroup.Radio value="long-distance">Long Distance Moving</RadioGroup.Radio> <RadioGroup.Radio value="furniture">Furniture Moving and Heavy Lifting</RadioGroup.Radio> </RadioGroup> ); }
Display type
standard (the default) renders the inline, borderless layout. control wraps each radio and its label in a padded, bordered card whose border becomes brand-colored when selected. Set type on the group to apply it to every radio, or on a single RadioGroup.Radio to override.
function RadioExample() { const [value, setValue] = React.useState('long-distance'); return ( <RadioGroup type="control" value={value} onChange={setValue} accessibilityLabel="Plans"> <RadioGroup.Radio value="long-distance">Long Distance Moving</RadioGroup.Radio> <RadioGroup.Radio value="furniture">Furniture Moving and Heavy Lifting</RadioGroup.Radio> <RadioGroup.Radio value="pool-table">Pool Table Moving</RadioGroup.Radio> </RadioGroup> ); }
Radios with a description
The description prop renders secondary text beneath a radio’s label and emphasizes the label. Radios with a description default to top-aligned indicators; this can be overridden with radioVerticalAlign.
function RadioExample() { const [value, setValue] = React.useState('basic'); return ( <RadioGroup type="control" value={value} onChange={setValue} accessibilityLabel="Plans"> <RadioGroup.Radio value="basic" description="Best for individuals getting started."> Basic </RadioGroup.Radio> <RadioGroup.Radio value="pro" description="For growing teams that need more."> Pro </RadioGroup.Radio> </RadioGroup> ); }
Vertical alignment
Indicators are vertically centered against single-line labels by default. When a label spans multiple lines, set radioVerticalAlign="top" (on the group or a single radio) to align the indicator with the first line. The prop only has a visible effect when the label is taller than the indicator.
<RadioGroup defaultValue="standard" radioVerticalAlign="top" accessibilityLabel="Shipping speed" > <RadioGroup.Radio value="standard"> Standard shipping. Arrives in five to seven business days, tracking included. </RadioGroup.Radio> <RadioGroup.Radio value="express"> Express shipping. Arrives in one to two business days, signature on delivery. </RadioGroup.Radio> </RadioGroup>
Horizontal orientation
Radios stack vertically by default. Set orientation="horizontal" on the group to lay them out in a row.
<RadioGroup defaultValue="yes" orientation="horizontal" accessibilityLabel="Are you available?" > <RadioGroup.Radio value="yes">Yes</RadioGroup.Radio> <RadioGroup.Radio value="no">No</RadioGroup.Radio> <RadioGroup.Radio value="maybe">Maybe</RadioGroup.Radio> </RadioGroup>
Custom label padding
The labelPadding prop expands a radio’s click/touch target by applying a CSS padding string to its label, mirroring the v1 prop. It is most useful with the control type, since it overrides the bordered card’s padding.
<RadioGroup defaultValue="long-distance" accessibilityLabel="Moving services"> <RadioGroup.Radio value="long-distance" labelPadding="16px 8px"> Long Distance Moving </RadioGroup.Radio> <RadioGroup.Radio value="furniture" labelPadding="16px 8px"> Furniture Moving and Heavy Lifting </RadioGroup.Radio> </RadioGroup>
Multi-column content
It’s possible to provide complex UIs to the children prop. Clicking on the content will select the related radio. Use radioVerticalAlign="top" to align the indicator with the first line of tall content.
function RadioExample() { const [value, setValue] = React.useState(undefined); return ( <RadioGroup value={value} onChange={setValue} accessibilityLabel="Pros"> <RadioGroup.Radio value="austin" radioVerticalAlign="center"> <div className="flex"> <div className="flex-none"> <UserAvatar imageUrl="https://randomuser.me/api/portraits/women/63.jpg" /> </div> <div className="flex items-center pl4" style={{ flex: '1 0 0%' }}> <div> <span className="b">Austin Entertainment LLC.</span> <p>DJs, photo booths, and photography for all of your event needs.</p> </div> <div className="b ml-auto">$120/hr</div> </div> </div> </RadioGroup.Radio> </RadioGroup> ); }
The full props for RadioGroup.Radio and RadioGroup are listed in the tables below.
Props
Radio
valuerequiredThe value submitted (and reported to the group's
onChange) when this radio is selected. Must be unique within the group. Maps to React Aria'svalue.TypestringchildrenText or elements that appear within the label. If
childrenis not provided, useaccessibilityLabelto label the radio for assistive technologies.TypeReact.ReactNodeidThe
idapplied to the underlying radio<input>element, e.g. to associate it with an external<label>. Maps to React Aria'sid.TypestringdescriptionSecondary text rendered beneath the label. When provided, the label is emphasized and the indicator aligns to the top. Maps to the
[data-has-description]attribute.TypestringisDisabledDisables this individual radio. Maps to React Aria's
isDisabled.TypebooleanhasErrorRenders this radio in an error state (red indicator and text). Defaults to the group's
hasErrorvalue when omitted. Maps to the[data-has-error]attribute.TypebooleantypeDisplay type for this radio. Defaults to the group's
typewhen omitted. SeeRadioGroupProps['type']for details. Maps to the[data-type]attribute.Type'standard' | 'control'radioVerticalAlignDetermines how the indicator is vertically aligned relative to
children. Defaults to the group'sradioVerticalAlign; when neither is set, radios with adescriptiondefault to'top'and the rest to'center'. Maps to the[data-align]attribute.Type'top' | 'center'labelPaddingPadding applied to the radio's clickable label, primarily to expand the click/touch target. Accepts a CSS padding string such as
8pxor8px 16px. When omitted, the component uses the standardized Thumbprint v2 spacing. With the'control'type it overrides the bordered card's padding.TypestringonKeyDownFunction that is called when the user presses a key while focused on the radio. Maps to React Aria's
onKeyDown.TypeAriaRadioFieldProps['onKeyDown']accessibilityLabelAccessible label for the radio. Only needed if non-textual children are provided (e.g. images, emojis). Maps to React Aria's
aria-label.TypestringdataTestIdA selector hook into the React component for use in automated testing environments.
Typestring
RadioGroup
childrenrequiredThe
RadioGroup.Radiooptions that make up the group.TypeReact.ReactNodevalueThe currently selected value. Provide together with
onChangefor a controlled group. Maps to React Aria'svalue.TypestringdefaultValueThe initially selected value for an uncontrolled group. Maps to React Aria's
defaultValue.TypestringonChangeFunction that runs when the selected radio changes. It receives the newly selected radio's
value. Maps to React Aria'sonChange.Type(value: string) => voidnameThe
nameattribute used when the group is submitted as part of an HTML form. React Aria applies it to the underlying radio inputs.TypestringaccessibilityLabelAccessible label for the group. Maps to React Aria's
aria-label.TypestringisDisabledDisables every radio in the group. Maps to React Aria's
isDisabled.TypebooleanisRequiredAdds the
requiredHTML attribute to the group. Maps to React Aria'sisRequired.TypebooleanhasErrorRenders the group in an error state. Marks the group invalid for assistive technologies (React Aria's
isInvalid) and cascades to each radio so the indicator and text turn red. Individual radios may override this with their ownhasErrorprop.TypebooleanDefaultfalseorientationLays the radios out vertically (default) or horizontally. Maps to React Aria's
orientationand the[data-orientation]attribute.Type'vertical' | 'horizontal'Default'vertical'typeDisplay type for every radio in the group.
'standard'renders the inline, borderless layout (the default).'control'wraps each radio and its label in a bordered, padded card whose border turns brand-colored when selected. Cascades to each radio, which may override it with its owntypeprop. Maps to the[data-type]attribute.Type'standard' | 'control'Default'standard'radioVerticalAlignDetermines how each radio's indicator is vertically aligned relative to its label. When unset, radios with a
descriptiondefault to'top'and the rest to'center'.'top'is best for multi-line labels or descriptions;'center'for single-line labels. Cascades to each radio, which may override it with its ownradioVerticalAlignprop. Maps to the[data-align]attribute.Type'top' | 'center'dataTestIdA selector hook into the React component for use in automated testing environments. Applied to the group's root element.
TypestringrowGapOverrides the default vertical space between radios. Accepts a CSS length such as
4pxor12px. Applied as the row gap on the wrapper around the radios.Typestring