Radio
A hand-drawn radio button, used inside a RadioGroup for single-selection.
Overview
A Radio represents one option in a single-select set. Used inside a RadioGroup, which derives
each radio’s checked state from its own value and enforces mutual exclusivity.
Anatomy
A sketchy ring drawn behind a real <input type="radio"> (React/Web Components) or a Pressable
with accessibilityRole="radio" (React Native). When checked, a second, smaller filled dot renders
inside the ring, computed directly from @ghds/sketch-core and sharing the ring’s measured size and
seed.
Variants & States
| State | Status | Notes |
|---|---|---|
| Unchecked | Implemented | — |
| Checked | Implemented | A filled dot renders inside the ring. |
| Disabled | Implemented | — |
| Loading | Not implemented | — |
Usage
Do
- Always use Radio inside a RadioGroup for real sets of mutually exclusive options.
- Give every Radio in a group the same name (React/Web Components) — RadioGroup does this for you automatically.
Don't
- Don't use a lone Radio outside a group to represent a single yes/no choice — use a Checkbox or Switch instead.
- Don't expect Web Components' same-name radios to get automatic mutual exclusivity the way native radio inputs do — that's exactly what gh-radio-group exists to provide, since the platform doesn't do it for custom elements.
Accessibility
- React: a real
<input type="radio">carries native keyboard behavior — arrow keys roving between radios sharing the samename, Space/click to select.RadioGroupsupplies that sharednamevia context so you don’t have to repeat it on everyRadio. - Web Components:
gh-radiorenders a real<input type="radio">in its shadow DOM, but same-namemutual exclusivity is not automatic for custom elements —<gh-radio-group>listens for a bubblingchangeevent and manually unchecks sibling radios. It does not propagatenameonto slotted children (Light DOM has no context-like mechanism) — repeatnameon each<gh-radio>explicitly. - React Native:
accessibilityRole="radio"+accessibilityState.checked, plus a directaria-checkedprop for the web target (React Native Web does not derivearia-checkedfromaccessibilityStatethe way it doesaria-disabled). - See the Accessibility Guide for general conventions.
Content
The label names the option itself (“Small”, “Medium”, “Large”), not an action.
Props API
React@ghds/react
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | — | Visible label, associated via Radix Label. |
value | string | — | This radio's own value — required. |
checked | boolean | — | Controlled checked state (standalone use). |
defaultChecked | boolean | — | Initial checked state when uncontrolled and standalone. |
disabled | boolean | false | Disables interaction. |
...rest | InputHTMLAttributes | — | All other native input attributes pass through. |
Web Components@ghds/web-components
| Prop | Type | Default | Description |
|---|---|---|---|
checked | boolean | false | Reflected attribute. |
disabled | boolean | false | Disables interaction and form participation. |
name | string | — | Submitted control name — repeat across a group. |
value | string | — | This radio's own value. |
label | string | — | Optional visible label, also the accessible name. |
React Native@ghds/react-native
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | — | Visible, accessible label — required. |
value | string | — | This radio's own value — required. |
checked | boolean | — | Controlled checked state (standalone use). |
defaultChecked | boolean | — | Initial checked state when uncontrolled and standalone. |
onCheckedChange | (checked: boolean) => void | — | Fires when this radio becomes checked (standalone use). |
disabled | boolean | false | Disables interaction. |
testID | string | — | Test handle for queries. |
Code examples
// React — @ghds/react
import { Radio } from '@ghds/react/radio';
import { RadioGroup } from '@ghds/react/radio-group';
import { useState } from 'react';
const [size, setSize] = useState('sm');
<RadioGroup label="Size" value={size} onValueChange={setSize}>
<Radio label="Small" value="sm" />
<Radio label="Medium" value="md" />
<Radio label="Large" value="lg" disabled />
</RadioGroup>;
<!-- Web Components — @ghds/web-components -->
<!-- No group value: repeat `name` on each child; the group unchecks siblings -->
<script type="module">
import '@ghds/web-components/radio';
import '@ghds/web-components/radio-group';
</script>
<gh-radio-group label="Size">
<gh-radio label="Small" name="size" value="sm" checked></gh-radio>
<gh-radio label="Medium" name="size" value="md"></gh-radio>
<gh-radio label="Large" name="size" value="lg" disabled></gh-radio>
</gh-radio-group>
// React Native — @ghds/react-native
import { Radio } from '@ghds/react-native/radio';
import { RadioGroup } from '@ghds/react-native/radio-group';
<RadioGroup label="Size" value={size} onValueChange={setSize}>
<Radio label="Small" value="sm" />
<Radio label="Medium" value="md" />
</RadioGroup>;
Live Demo
React and Web Components render live below, each wrapped in their own RadioGroup/<gh-radio-group>.
React Native can’t run in a browser without additional react-native-web wiring, so its usage is
shown as a code sample instead.