Radio Button
A group of round options where exactly one can be chosen, like Standard or Express delivery. Learn when to use it instead of a dropdown, checkbox or segmented control, when to preselect, how the keyboard should work, and the grouping bugs testers find.
Standard or Express delivery? Card or PayPal? Window or aisle? Whenever an app asks you to pick exactly one answer from a short list, you’re probably looking at radio buttons. The name comes from old car radios, where pushing in one station button popped the others out.
That “only one” promise is the whole point, and it’s exactly what breaks: two payment methods selected at once, a total that ignores your choice, an order placed with nothing selected. Let’s learn how to catch it.
What is a radio button?
A radio button is a round option inside a group, where exactly one option can be selected. Selecting one automatically unselects the others: the options are mutually exclusiveMutually exclusiveOptions where choosing one automatically means the others can't be chosen.Open in glossary →. A single radio button on its own makes no sense. It always lives in a group with a question (“Delivery”, “Payment method”).
You’ll see radio groups used for:
- Delivery and shipping options: Standard / Express / Next day, each with a date and a price.
- Payment methods: Card / PayPal / Pay on delivery.
- Plans and sizes: Monthly / Yearly, Small / Medium / Large.
- Survey and preference questions: Seat preference, “How did you hear about us?”.
Anatomy
Delivery speed
- 1
Group label (question)
Says what you are choosing. On the web it is the <legend> of a <fieldset>. Without it, "Standard" and "Express" mean nothing to a screen reader user.
- 2
Selected indicator
An outer ring with a filled inner dot. Only one option in the group can have it.
- 3
Unselected circle
An empty ring. Round, never square: the shape tells users "pick one".
- 4
Label
The option itself. Tapping it must select the option too.
- 5
Supporting text (optional)
Details that help the user decide: delivery dates, a price, a fee.
- 6
Touch target
The tappable area: ideally the whole row, at least 44×44 pt (iOS) or 48×48 dp (Android).
Two rules that define a radio group
- Exactly one can be selected. Selecting a second option unselects the first.
- Once something is selected, you can’t go back to “nothing”. Tapping a selected radio again does nothing. If “no answer” is valid, the group needs an option for it, like None or Prefer not to say.
Both rules are worth a test case of their own.
Radio vs dropdown vs segmented control vs checkbox
| Component | Choices | Visible? | Best for |
|---|---|---|---|
| Radio buttons | Exactly one of ~2–6 | All at once | Forms, options that need a description or price |
| Dropdown | One of many | Hidden until opened | Long lists: country, size chart, time slot |
| Segmented control | One of 2–5 short labels | All at once | Switching views or filters immediately |
| Checkbox | Zero, one or many | All at once | Picking several items, or one yes/no |
Dev note
The web calls it a radio button (<input type="radio">). Material Design calls it a radio button too. Apple’s iOS guidelines don’t have one at all: iPhone apps use checkmark lists, pickers or menus for single choice. Some teams say option button. In bug reports, use the name your team uses.
Designs & variants
Every group here works. Click the labels, and try Tab + arrow keys.
Material 3 radio button
AndroidA 20 dp circle inside a 48 dp touch target. The whole row is usually tappable, and a round highlight shows hover, press and focus.
Auto-Lock
iOS: checkmark list
iOSiOS has no radio button control. Single choice is a list with a checkmark on the selected row, like Settings › Display & Brightness › Auto-Lock, or a menu.
Native web radios
Web<input type="radio"> elements that share one name, inside a <fieldset> with a <legend>. Grouping, arrow keys and screen reader support come for free.
Radio cards with details
Web · AppsEach option is a card with a description and a price. The whole card is the label, so tapping the text selects it. One option is disabled, with the reason shown.
Required group with an error
AllNo option is preselected, so the user must choose. If they skip it, the error text names the problem and is linked to the group.
- Unselected
- Selected
- Focused
- Disabled
- Disabled on
- Error
All the states
AllDisabled radios keep showing whether they are selected. A focused radio needs a visible ring for keyboard users.
Keyboard: one tab stop, arrows inside
Radio groups work differently from every other form control on the keyboard. Try it: click into “Passenger name”, then use Tab, the arrow keys and Space, and watch the log.
Key log
- Click "Passenger name", then press Tab…
- Tab enters the group once, on the selected radio (or the first one if nothing is selected).
- Arrow keys move focus and selection inside the group, and wrap around.
- Tab again leaves the group and goes to Continue.
- Space selects the focused radio if none is selected yet.
This is what native radio buttons do for free. Custom-built radio groups (made of divs or styled buttons) often get it wrong: every option becomes its own tab stop, or the arrow keys do nothing. Both are bugs. See keyboard navigationKeyboard navigationUsing a website with only the keyboard: Tab to move, Enter or Space to activate, Esc to close.Open in glossary →.
When to use it
Radio buttons are the right choice when:
- The user must pick exactly one option.
- There are few options (roughly 2–6), so they can all be shown at once.
- Seeing the options side by side helps the decision: prices, delivery dates, plan details.
- The choice is part of a form and applies when the user presses Continue, Pay or Save.
Should one option be preselected?
Many design guidelines recommend selecting a sensible default, because it saves a tap and the user can’t accidentally skip the question. But a default is a decision made for the user, so it has to be a safe one.
| Preselect when… | Don’t preselect when… |
|---|---|
| There’s a safe, common choice: Standard (free) delivery | The default costs money: insurance, a paid upgrade |
| The setting already has a value: the user’s saved card | The question is personal or sensitive: title, gender, date of birth |
| A wrong default is easy to notice and cheap to change | You need to know the user really chose (consent, legal answers) |
If there’s no default, the form must block submission until the user chooses, with a clear error. Whatever the rule, it should be written in the spec, so you can test against it.
Good and bad uses
Safe default preselected
Delivery
Free Standard delivery is what most people want. Express is one tap away.
Paid extra preselected
Travel insurance
Users who don’t read carefully pay €24 they never chose. Leave it unselected and make the user decide.
A few options, all visible
Size
Three options with short details. Users compare them at a glance.
A long list as radios
Country
27 countries as radio buttons fill the screen. Use a dropdown or a searchable list.
Single yes/no as a checkbox
Tap again to untick it.
A checkbox can be ticked and unticked.
A lone radio button
Selected… and now there is no way to unselect it.
Once it’s selected, it can never be unselected. A lone radio is always a bug.
Also skip radio buttons when the choice should switch content immediately on the same screen, like List / Map. That’s a segmented control.
Common bugs
| # | Bug | What you see | Typical severity |
|---|---|---|---|
| 1 | Broken grouping | Two options in the same group can be selected at once (often a mismatched name on the web) | High |
| 2 | Wrong keyboard behavior | Arrow keys don’t move the selection, or every option is a separate tab stop | High (for affected users) |
| 3 | Dependent values not updated | Switching Express → Standard doesn’t change the total, delivery date or fee | High |
| 4 | Nothing selected, still submits | No default, and the form goes through with no choice (or sends null) | High |
| 5 | Tiny target | Only the circle reacts; tapping the label, description or price does nothing | Medium |
| 6 | Selection lost | After a validation error, Back, rotation or reload the choice jumps back to the default | Medium |
| 7 | Bad default | A paid or sensitive option is preselected, or the default doesn’t match the spec | Medium–High |
| 8 | Disabled option without reason | An option is greyed out, but nothing says why (“Not available for your address”) | Low–Medium |
| 9 | Disabled option still selectable | A greyed-out option can be picked by keyboard or by tapping its label | Medium |
| 10 | Screen reader silence | No group name, no “radio button”, no “selected”, no “2 of 3” | High (for affected users) |
Find the bug
This web checkout has six bugs in its two radio groups. Explore it like a tester:
- Switch between all three shipping options and read the total every time.
- Tap the shipping names, descriptions and prices, not just the circles.
- Use only the keyboard: Tab into each group and try the arrow keys.
- Try to pick two payment methods.
- Press Place order with the email empty, and again without choosing a payment method.
🐞 Bug hunt: Checkout
This demo has 6 hidden bugs. Explore it like a tester, then check your findings.
Use the mouse and the keyboard (Tab, arrows, Space).
- 1
Hint: Select Card, then Pay on delivery.
- 2
Hint: Tab to a shipping option and press the Down arrow.
- 3
Hint: Select Express and compare the Shipping line with the Total. Then select Next day.
- 4
Hint: Enter an email, don’t choose any payment method, and press Place order.
- 5
Hint: Click the words "Next day" or the price "€12.99".
- 6
Hint: Select Next day, leave the email empty and press Place order.
How to test it
Checklist
Radio button test checklist
0 of 12 checked
Example test cases
TC-RB-001Only one shipping option can be selectedpositivehigh
Steps
- Open Checkout
- Select Express
- Select Next day
- Select Standard
Expected result
After each step exactly one option is selected: the last one tapped.
TC-RB-002Total updates for every shipping optionpositivehigh
Preconditions
- Cart subtotal is €54.00
Steps
- Select Express
- Select Next day
- Select Standard
Expected result
Total reads €60.99, then €66.99, then €54.00. The shipping line and the total always agree.
TC-RB-003Order cannot be placed without a payment methodnegativehigh
Preconditions
- No payment method is preselected
Steps
- Fill in a valid email
- Leave Payment empty
- Press Place order
Expected result
An error "Choose a payment method" is shown next to the Payment group and announced. No order is created.
TC-RB-004Payment options form one groupnegativehigh
Steps
- Select Card
- Select Pay on delivery
- Select PayPal
Expected result
Each new selection unselects the previous one. Only PayPal is selected at the end.
TC-RB-005Choice is kept after a validation erroredgemedium
Steps
- Select Next day
- Leave the email empty
- Press Place order
Expected result
The email error is shown. Next day is still selected and the total still includes €12.99.
TC-RB-006Keyboard navigation inside the grouppositivemedium
Preconditions
- Web, desktop browser
- Standard is selected
Steps
- Press Tab until the Shipping group is focused
- Press the Down arrow twice
- Press Tab
Expected result
Focus enters on Standard. Down selects Express, then Next day. Tab leaves the group and moves to Payment.
TC-RB-007Whole option card is clickablepositivelow
Steps
- Click the description text of Express
- Click the price of Next day
Expected result
Express is selected after the first click, Next day after the second.
Tip: click a test case to expand it.
Accessibility
- Name the group. Otherwise a screen readerScreen readerSoftware that reads the screen aloud for blind and low-vision users, like VoiceOver or TalkBack.Open in glossary → user hears “Express, radio button” without knowing the question.
Dev note
On the web, put radios in a<fieldset>with a<legend>, or userole="radiogroup"witharia-labelledby. - Group by name. Native radios belong together only if they share the same
name. A typo or a “new” option with a different name breaks the group. - Connect labels. The label makes the text clickable and gives the radio its spoken name. On mobile the whole row should be the touch targetTouch targetThe area of the screen that responds to a tap, which should be large enough for a finger.Open in glossary →: at least 44×44 pt on iOS and 48×48 dp on Android. On the web, WCAG 2.5.8 (AA) asks for at least 24×24 CSS px; 44×44 px (2.5.5, AAA) is the comfortable target.
Dev note
<label for>or a wrapping<label>. - Keyboard (web): Tab enters the group once, arrow keys move focus and selection, Space selects the focused option. See ARIAARIAHTML attributes that tell assistive technology what custom elements are and what state they're in.Open in glossary → and focus orderFocus orderThe order in which the Tab key moves through interactive elements.Open in glossary →.
Dev note
Custom radios needrole="radio",aria-checkedand a rovingtabindexto behave the same. - Announce state and position. A good screen reader output sounds like “Express, radio button, not selected, 2 of 3”.
- Errors. If a required group is left empty, show the error text next to the group, and move focus or announce it.
Dev note
Link the error witharia-describedby. - Not color alone. The inner dot shows the selection. The ring needs at least 3:1 contrast against the background (WCAGWCAGThe Web Content Accessibility Guidelines, the main international standard for accessibility.Open in glossary →).
Mobile vs web
- No native radio button. Single choice is shown as a list with a checkmark on the selected row (Settings › Display & Brightness › Auto-Lock), a picker (inline, menu or wheel style), or a menu.
Dev note
The picker is a SwiftUIPicker. - VoiceOver should announce the selected row as “Selected”. Silence is a bug.
- Some apps draw custom radio circles. Check the whole row is tappable and that VoiceOver reads the state.
- Test with the largest Dynamic Type sizes: option descriptions with prices must wrap, not truncate.
- Material 3 radio button: a 20 dp circle inside a 48 dp touch target. The whole row should be tappable, and TalkBack should understand the group.
Dev note
RadioButton. In Compose, rows should useModifier.selectable(role = Role.RadioButton)and the groupModifier.selectableGroup(). - TalkBack should announce the label, “radio button” and whether it’s selected. Test with font scale, display size and dark theme.
- Check the selection after rotation and after the app was killed in the background.
- Android dialogs often use radio lists (“Choose ringtone”) with OK / Cancel. Cancel must discard the change.
- Native
<input type="radio">gives grouping, keyboard and screen reader support. Inspect thenameattributes with DevToolsDevToolsDeveloper tools built into browsers for inspecting pages, network traffic and errors.Open in glossary →: all options of one question must share it. - Custom radio cards are very common. Check they behave like native radios with the keyboard.
- The browser doesn’t stop a form from submitting with nothing selected unless
requiredis set, and the server must validate too. - Test browser Back and reload: some browsers restore form state, others don’t. Check what the spec expects.
Check your understanding
Quick quiz
0/5 answered · 0 correct
1.A checkout lets you choose Standard, Express or Next day delivery, each with a date and price. Which control fits best?
2.On a web form you can select both "Card" and "Pay on delivery". What is the most likely cause?
3.What should the arrow keys do inside a radio group on the web?
4.Which option should NOT be preselected?
5.You switch from Express to Standard, the shipping line says "Free", but the total still includes €6.99. What kind of bug is this?
Key takeaways
- Radio buttons are for exactly one choice from a small, visible set. Many options → dropdown. Several choices → checkboxes. Instant view switch → segmented control.
- Once selected, a group can’t go back to “nothing”. A lone radio is always wrong.
- Preselect only a safe, free, common default. No default → the form must require a choice, on the server too.
- Test grouping (only one selected), everything calculated from the choice (totals, dates), the whole row as a target, and state after errors and Back.
- On the web: one tab stop per group, arrow keys inside, a legend for the question, and a shared name for all options.
Finished this page?Nice work, this page is complete!
Mark it as done to track your progress. It's saved in this browser, no account needed.
Its review cards are now in Bites ⚡