Radio
Exactly one from a set shown in full, with nothing chosen until somebody chooses it.
Overview
A radio group asks a question whose answers are mutually exclusive and shows all of them, which is what separates it from a Select: the options are part of the question. Two things about it are easy to get wrong, and both put an answer in the record that nobody gave.
Which of the three to reach for — how many may be chosen, and when the choice takes effect — is decided on Selection controls.
Nothing is selected by default
A clinical radio group starts with nothing selected. A pre-selected option is an answer nobody gave, and in the record it is indistinguishable from one somebody did.
- This is the blank-field argument arriving through a different control. A default is worse than a blank, because a blank at least looks unanswered.
- “Unknown” is an option, not an empty group. Because nothing is pre-selected, a genuine non-answer needs somewhere to live — or clinicians supply one by guessing.
- A two-option group is still a group where both answers need to be visible. Collapsing “yes or no” into a single checkbox makes the unticked state mean two things: no, and not yet asked.
Arrowing through a radio group answers it
A radio group is traversed with the arrow keys, and on the web arrow keys in a radio group move focus and select. Moving from Yes to Unknown selects No on the way. This is native behaviour, endorsed by ARIA's authoring practices, and this system keeps it — the alternative is a control that behaves unlike every other radio group the clinician has ever used.
A change event from a radio group is a selection, not an answer. Three keystrokes produce three changes and one intention. Nothing may be recorded, submitted, acknowledged or billed on the strength of one.
- This is the same objection as arrowing through tabs, and the same as the one Editing the draft makes to dwell timers: an interaction the interface generated is not evidence of a decision the reader made.
- Where the intermediate answer is itself consequential — a group whose options fire an order, a mode change, or a clinical flag — those are not radios. A set of options that each do something is a set of buttons.
- Nothing is selected by default. A pre-selected radio is an answer nobody gave, and it is indistinguishable in the record from one somebody did give — the argument in Text field, arriving through a different control. An unanswered question stays unanswered.
- “Unknown” is an option, not an empty group. Because nothing is pre-selected, the group needs somewhere for a genuine non-answer to live, or clinicians will supply one by guessing.
What the component enforces, and what it only asks for
Almost none of the safety argument on this page is held by the component, and the reason is worth stating plainly: a radio is controlled by whoever passes checked.
| Rule | Held by | How |
|---|---|---|
| A group states its shared question | The component | RadioGroup is a real fieldset with a legend.Asserted: RadioGroup: the shared question is a real legend |
| One answer, one tab stop, arrows inside | The component | RadioGroup gives its radios a shared name, which is what makes the platform treat them as one question. It did not: a caller who left name off got two answers checked at once to a yes/no question and a tab stop per option. An explicit name still wins. Driven in a browser.Asserted: Radio: a group whose radios carry no name still allows only one answer |
| The whole label row is the target | The component | From the shared base file, at 44 px. Asserted: Radio: the label row is the target, not the control |
| An unavailable option says why, and stays reachable | The component | disabled is not a prop, as on Button. unavailable={{ reason }} keeps the control focusable, describes it by the reason, and ignores Space and clicks. It accepted a bare native disabled: off, unreachable by keyboard, and silent about why.Asserted: Checkbox, radio and switch cannot be switched off without a reason |
| Nothing is selected by default | You | checked and defaultChecked pass straight through. A pre-selected clinical radio is an answer nobody gave, and this component will render one without complaint. |
| A change event is not treated as an answer | You | Arrow keys select as they traverse, so moving from Yes to Unknown fires a change for No. If your handler writes to the record, it has just recorded that the patient is not pregnant. |
| “Unknown” is one of the options | You | The options are whatever you render. A group with nowhere to put a genuine non-answer produces guesses. |
A rule stated on a component page reads as a rule the component keeps. 2 of these 6 are; 3 are obligations this page was quietly handing to the reader. Three of the six are yours, and they are the three that decide what ends up in the record. The interaction gate asserts the traversal behaviour precisely because the component cannot protect you from it.
Do's and don'ts
Yes · No · Unknown
recorded on submit
Two change events passed through on the way. Neither reached the record, because the record is written when the form is.
Yes · No · Unknown
saved on change · 14:22 · 14:22 · 14:22
Three entries, one intention, and the middle one says the patient is not pregnant. Nobody typed that.
Pregnant?
○ Yes ○ No ○ Unknown
Nothing selected. The question is visibly unanswered until somebody answers it.
Pregnant?
○ Yes ● No ○ Unknown
“No” pre-selected. In the record this is indistinguishable from a clinician having asked and been told no, and nobody asked.
Sampling interval
● 0/1 h algorithm
○ 0/3 h algorithm
Both options visible. The reader chooses between things they can see.
Sampling interval: 0/1 h algorithm ▾
A two-option choice hidden in a select. The alternative is invisible until opened, and a reader who never opens it never learns there was a choice.
Accessibility
- A real
fieldsetwith alegend. A group of radios whose shared question is only a nearby heading is a group a screen-reader user answers without knowing what was asked. - One tab stop for the group, with arrow keys moving inside it — the platform's own behaviour, which is also the behaviour every clinician already has.
- The hint is referenced by
aria-describedbyrather than merely displayed. - The whole label row is the target, and the options are never truncated: an option a reader cannot read is one they cannot choose.
Outcomes of use
- Effectiveness — the answer in the record is the answer somebody gave.
- Efficiency — all options visible at once, so choosing costs a look rather than a menu.
- Satisfaction — no sense of having been answered for. Attributes believed to contribute to an outcome; the outcome is settled by observing real use, not by this page — see Usability & context of use.
Clinical safety notes
Trace these in your risk file (ISO 14971) and usability engineering file (IEC 62366-1). Which of these the component keeps and which are yours is stated in the guarantees table; a control the component does not keep is one your risk file must show you keep.
- Nothing is pre-selected where the choice is clinical. (you) Mitigates: an unasked question recorded as answered.
- A change event is a selection, not an answer. (you) Mitigates: three keystrokes filing three answers, the middle one saying something nobody meant — nothing may be recorded, submitted or acknowledged on the strength of one.
- A non-answer has its own option. (you) Mitigates: a guess supplied because the form had nowhere to put “not known”.
Implementation
$ npx shadcn@latest add https://md.notjustany.tech/r/radio.json
<RadioGroup legend="Pregnant?">
// no defaultChecked: an unanswered question stays unanswered
// the group names its radios, so they are one question without a `name` here
<Radio label="Yes" />
<Radio label="No" />
<Radio label="Unknown" />
</RadioGroup>
Related
- Selection controls — which of the three to use.
- Checkbox — zero or more instead of exactly one.
- Select — when the option list is too long to show in full.
- Text field — the same argument about what an unanswered field means.