Topic 5.2
Checkboxes, Radio Buttons and Selects
In one line
Checkboxes and radios are controlled with checked (a boolean), selects with value (a string, or an array for multiple), and every value that comes out of a field, even a number field, is a string you convert yourself.
Think of it like this
Ordering at a juice shop. Checkboxes are the extras (ice, mint, honey): take any number of them, each one yes or no. Radio buttons are the cup size: small, medium or large, exactly one. A select is the menu board: pick one item from a long list. Each kind of choice has its own React pattern.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Checkbox
- A box that is ticked or not. Use one for a yes/no choice, or several for "pick any".
- checked prop
- The boolean that tells React whether a checkbox or radio is on.
- e.target.checked
- In a checkbox or radio's handler,
trueif it is now on,falseif off. - Radio button
- One option in a group where only one can be chosen. All radios in a group share a
name. - name attribute
- The field's name. It groups radios together and is the key used when a form is sent or read with FormData.
- Select
- A drop-down list (
<select>with<option>s). In React itsvalueprop decides which option is shown. - Multiple select
- A
<select multiple>where several options can be picked. In React itsvalueis an array. - valueAsNumber
- A DOM property on number (and date) inputs that gives the value as a number, or
NaNwhen it's empty or not valid.
Step by step
01A yes/no checkbox
One boolean state, checked instead of value, and e.target.checked in the handler.
const [agree, setAgree] = useState(false);
<label>
<input type="checkbox" checked={agree} onChange={(e) => setAgree(e.target.checked)} />
I agree to the terms
</label>
<button disabled={!agree}>Continue</button>02A group of checkboxes as an array
Draw the boxes from a list of options. Each box is checked if its value is in the array; the handler adds or removes it.
const options = ["ice", "mint", "honey"];
const [extras, setExtras] = useState([]);
function toggle(e) {
const { value, checked } = e.target;
setExtras(checked ? [...extras, value] : extras.filter((x) => x !== value));
}
{options.map((o) => (
<label key={o}>
<input type="checkbox" value={o} checked={extras.includes(o)} onChange={toggle} /> {o}
</label>
))}03A radio group is one value
Every radio shares name="size". One state string decides which one is on.
{["S", "M", "L"].map((s) => (
<label key={s}>
<input type="radio" name="size" value={s}
checked={size === s} onChange={(e) => setSize(e.target.value)} /> {s}
</label>
))}04A select is controlled on the select
Put value and onChange on <select>. The options only need their own value.
<select value={city} onChange={(e) => setCity(e.target.value)}>
<option value="">Choose a city…</option>
<option value="pune">Pune</option>
<option value="delhi">Delhi</option>
</select>05Convert numbers where you use them
Keep what the user typed as a string, so the field can be empty or half-typed. Turn it into a number only for maths, and handle the empty case.
const [qty, setQty] = useState("1");
const n = Number(qty); // "" becomes 0, "2" becomes 2
const total = qty === "" ? 0 : n * 120;Try it yourself
- 1
Add a size
In the juice order, add
XL: "Extra large"toSIZES. Predict: do you need to touch the JSX? (No: the radios are drawn from the object.) - 2
Limit the extras
Make it impossible to choose more than two extras: give each extra checkbox
disabled={!extras.includes(x) && extras.length >= 2}. Tick two and watch the third grey out. - 3
Use valueAsNumber
In the quantity example, store
e.target.valueAsNumberinstead ofe.target.valueand clear the field. What happens? (The state becomesNaN, the total showsNaN, and React warnsReceived NaN for the \value\attribute. If this is expected, cast the value to a string.That's why keeping the string is safer.)
Code & diagrams
Four kinds of state for four kinds of choice: a string for the select, a string for the radio group, an array for the checkbox group and a boolean for one checkbox. The summary is worked out from all four.
Preview
Console
Nothing logged yet.Clear the field: the state becomes `""` and the total shows ₹0 instead of NaN, because the code handles the empty case. If state held a number, an empty field would have nothing sensible to store.
Preview
Console
Nothing logged yet.Expected console output
qty is "1" of type string`value` is an array, and the handler builds a new array from `selectedOptions`. Hindi starts selected because it is in the initial array.
Preview
Console
Nothing logged yet.Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Read value instead of checked
In the juice order, change the gift box handler to (e) => setGift(e.target.value). Tick the box, then try to untick it.
Break #2
Mark the chosen option with selected
Remove value and onChange from the select and write <option value="mango" selected>Mango</option>.
Break #3
Give a multiple select one string
In the languages example, change the initial state to useState("hi").
Myth vs fact
Myth
Radios in a group need one state variable each.
Fact
A radio group is one choice, so it is one state value. Each radio compares itself to it: checked={size === "M"}.
Myth
type="number" gives you numbers.
Fact
The DOM value of every input is a string. valueAsNumber exists on the element, but storing the string is easier for half-typed values like - or empty fields.
Myth
You need selected on an option to choose it.
Fact
In React the <select>'s value (or defaultValue) chooses the option. selected on options triggers a warning.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
For checkboxes and radios React's
onChangeis wired to the nativeclickevent, because older browsers didn't firechangereliably. If you callpreventDefault()in a checkbox's click handler, the browser reverts the tick, and React's state and the DOM can briefly disagree. Don't prevent default on checkboxes; control them through state. - ▸
A checkbox has a third visual state, indeterminate (a dash), used for "some children are checked". There is no HTML attribute or React prop for it: set
el.indeterminate = trueon the DOM node through a ref (Phase 8). - ▸
Number inputs have pitfalls beyond strings: scrolling the mouse wheel over a focused number field changes its value in some browsers, and they accept
e,+and-. For things like card numbers or PINs usetype="text"withinputMode="numeric", which shows a number keypad on phones without number-field behaviour.
Remember this
- 1
A checkbox is on or off, so React controls it with
checked, notvalue:<input type="checkbox" checked={agree} onChange={(e) => setAgree(e.target.checked)} />. Reade.target.checked(a boolean).e.target.valueof a checkbox is itsvalueattribute, which is the string"on"if you didn't set one, so reading it is a classic bug. - 2
A group of checkboxes for a list of options is usually stored as an array of the chosen values,
["mint", "honey"]. A box is checked when the array includes its value:checked={extras.includes("mint")}. On change, add the value ([...extras, value]) or remove it (extras.filter((x) => x !== value)), neverpush(Topic 4.7). - 3
Radio buttons in one group share the same
nameattribute, which tells the browser that only one can be on. In React keep one state value for the whole group, and give each radiochecked={size === "M"}andonChange={(e) => setSize(e.target.value)}. Heree.target.valueis right: it is thevalueattribute of the radio the user picked. - 4
A select (drop-down) is controlled with
valueon the<select>itself, notselectedon an<option>:<select value={city} onChange={(e) => setCity(e.target.value)}>. React warns if you useselectedon options. An empty first option such as<option value="">Choose…</option>is the usual "nothing picked yet" state. - 5
A multiple select (
<select multiple>) lets users pick several options (with Ctrl or Cmd). Itsvaluemust be an array. Read the picked values withArray.from(e.target.selectedOptions, (o) => o.value). Many users find multiple selects awkward, so a list of checkboxes is often friendlier. - 6
Every value is a string.
<input type="number">still givese.target.value === "3", and"3" + 1is"31". Keep the string in state (so the user can type-,1.or clear the field) and convert where you calculate:Number(qty), or reade.target.valueAsNumber(which isNaNwhen the field is empty). Selects and radios also give strings, even if the options look like numbers.
Explain it without notes
How do you control a checkbox in React, and why is e.target.value the wrong thing to read?
How do you build a radio group in React? What does the name attribute do?
How do you control a <select multiple> and read its chosen values?
Why should a number field's state usually be a string?
Practice
Build a controlled "Remember me" checkbox and a paragraph that says You will stay signed in or You will be signed out.
Build a radio group for delivery speed (standard, express) that shows the price: ₹40 or ₹120.
Build a select of three themes (light, dark, sepia) that sets a className on a preview div.
Trade-offs
- ↔
Radios show every option at once and need one click; a select saves space for long lists but hides the options. Use radios for 2–5 options, a select for more.
- ↔
A multiple select is compact but hard to use (Ctrl-click, easy to lose a selection by a plain click). A list of checkboxes is usually clearer, at the cost of more space.
Done when you can
I can control a checkbox with
checkedande.target.checked.I can store a group of checkboxes as an array without mutating it.
I can build a radio group with one state value and a shared
name.I can control a select and a multiple select.
I keep number fields as strings and convert them where I calculate.