Command Palette

Search for a command to run...

Hectal
PHASE 5Intermediate ~27 min· topic 2 of 7

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, true if it is now on, false if 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 its value prop decides which option is shown.
Multiple select
A <select multiple> where several options can be picked. In React its value is an array.
valueAsNumber
A DOM property on number (and date) inputs that gives the value as a number, or NaN when 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.

Terms.jsxwhole filejsx
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.

Extras.jsxwhole filejsx
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.

Size.jsxwhole filejsx
{["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.

City.jsxwhole filejsx
<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.

Qty.jsxwhole filejsx
const [qty, setQty] = useState("1");
const n = Number(qty);          // "" becomes 0, "2" becomes 2
const total = qty === "" ? 0 : n * 120;
Convert numbers where you use themdiagram
Rendering diagram…

Try it yourself

  1. 1

    Add a size

    In the juice order, add XL: "Extra large" to SIZES. Predict: do you need to touch the JSX? (No: the radios are drawn from the object.)

  2. 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. 3

    Use valueAsNumber

    In the quantity example, store e.target.valueAsNumber instead of e.target.value and clear the field. What happens? (The state becomes NaN, the total shows NaN, and React warns Received 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

Juice order: checkbox group, radios and a select New tab

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

Press Run to see the page.

Console

Nothing logged yet.
Number inputs give strings New tab

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

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

qty is "1" of type string
A multiple select keeps an array New tab

`value` is an array, and the handler builds a new array from `selectedOptions`. Hindi starts selected because it is in the initial array.

Preview

Press Run to see the page.

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.

terminal
$ Preview
── what you'll see ──
The box ticks once and can never be unticked. The summary keeps saying (gift).

Break #2

Mark the chosen option with selected

Remove value and onChange from the select and write <option value="mango" selected>Mango</option>.

terminal
$ Console
── what you'll see ──
Use the `defaultValue` or `value` props on <select> instead of setting `selected` on <option>.

Break #3

Give a multiple select one string

In the languages example, change the initial state to useState("hi").

terminal
$ Console
── what you'll see ──
The `value` prop supplied to <select> must be an array if `multiple` is true.
 
Check the render method of `Languages`.

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 onChange is wired to the native click event, because older browsers didn't fire change reliably. If you call preventDefault() 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 = true on 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 use type="text" with inputMode="numeric", which shows a number keypad on phones without number-field behaviour.

Remember this

  1. 1

    A checkbox is on or off, so React controls it with checked, not value: <input type="checkbox" checked={agree} onChange={(e) => setAgree(e.target.checked)} />. Read e.target.checked (a boolean). e.target.value of a checkbox is its value attribute, which is the string "on" if you didn't set one, so reading it is a classic bug.

  2. 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)), never push (Topic 4.7).

  3. 3

    Radio buttons in one group share the same name attribute, which tells the browser that only one can be on. In React keep one state value for the whole group, and give each radio checked={size === "M"} and onChange={(e) => setSize(e.target.value)}. Here e.target.value is right: it is the value attribute of the radio the user picked.

  4. 4

    A select (drop-down) is controlled with value on the <select> itself, not selected on an <option>: <select value={city} onChange={(e) => setCity(e.target.value)}>. React warns if you use selected on options. An empty first option such as <option value="">Choose…</option> is the usual "nothing picked yet" state.

  5. 5

    A multiple select (<select multiple>) lets users pick several options (with Ctrl or Cmd). Its value must be an array. Read the picked values with Array.from(e.target.selectedOptions, (o) => o.value). Many users find multiple selects awkward, so a list of checkboxes is often friendlier.

  6. 6

    Every value is a string. <input type="number"> still gives e.target.value === "3", and "3" + 1 is "31". Keep the string in state (so the user can type -, 1. or clear the field) and convert where you calculate: Number(qty), or read e.target.valueAsNumber (which is NaN when the field is empty). Selects and radios also give strings, even if the options look like numbers.

Explain it without notes

01

How do you control a checkbox in React, and why is e.target.value the wrong thing to read?

02

How do you build a radio group in React? What does the name attribute do?

03

How do you control a <select multiple> and read its chosen values?

04

Why should a number field's state usually be a string?

Practice

01

Build a controlled "Remember me" checkbox and a paragraph that says You will stay signed in or You will be signed out.

02

Build a radio group for delivery speed (standard, express) that shows the price: ₹40 or ₹120.

03

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 checked and e.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.