React Checkbox and Radio

Checkboxes and radio buttons are controlled through their checked attribute rather than value, which trips up many developers coming from plain HTML forms.

Checkboxes Use checked, Not value

A checkbox's meaningful state is boolean — checked or not — so a controlled checkbox binds a boolean state variable to the checked prop, and reads the new boolean back from e.target.checked (not e.target.value, which is always the fixed string 'on').

A Single Controlled Checkbox

import { useState } from 'react';

function NewsletterOptIn() {
  const [subscribed, setSubscribed] = useState(false);

  return (
    <label>
      <input
        type="checkbox"
        checked={subscribed}
        onChange={(e) => setSubscribed(e.target.checked)}
      />
      Subscribe to the newsletter
    </label>
  );
}

export default NewsletterOptIn;
Note: Setting checked without an onChange handler makes React log a warning and effectively freezes the box, since there's nothing to update the controlling state. Pair checked with onChange, or use defaultChecked for an uncontrolled checkbox.

A Group of Independent Checkboxes

When several checkboxes represent independent yes/no choices (not mutually exclusive), the cleanest state shape is usually an object or array of the currently checked values, toggled by each box's own onChange.

Checkbox Group Backed by an Object

import { useState } from 'react';

function ToppingsPicker() {
  const [toppings, setToppings] = useState({
    cheese: true,
    olives: false,
    peppers: false,
  });

  function toggle(name) {
    setToppings((prev) => ({ ...prev, [name]: !prev[name] }));
  }

  return (
    <fieldset>
      <legend>Toppings</legend>
      {Object.keys(toppings).map((name) => (
        <label key={name}>
          <input
            type="checkbox"
            checked={toppings[name]}
            onChange={() => toggle(name)}
          />
          {name}
        </label>
      ))}
    </fieldset>
  );
}

export default ToppingsPicker;

Radio Buttons Model a Single Choice

Radio buttons that share the same name form a mutually exclusive group where exactly one can be checked. In React you don't set 'checked' on each radio individually by hand — instead, one state variable holds the currently selected value, and every radio's checked prop is simply an equality check against it.

A Controlled Radio Group

import { useState } from 'react';

function SizePicker() {
  const [size, setSize] = useState('medium');
  const sizes = ['small', 'medium', 'large'];

  return (
    <fieldset>
      <legend>Size</legend>
      {sizes.map((s) => (
        <label key={s}>
          <input
            type="radio"
            name="size"
            value={s}
            checked={size === s}
            onChange={(e) => setSize(e.target.value)}
          />
          {s}
        </label>
      ))}
    </fieldset>
  );
}

export default SizePicker;
  • Checkbox: boolean state, checked prop, reads e.target.checked
  • Radio group: string (or other) state, checked = state === option, reads e.target.value
  • Always give radios in the same group an identical name attribute
  • fieldset + legend gives assistive technology a clear group label for free
Input TypeState Shapechecked Prop Logic
Single checkboxbooleanchecked={isChecked}
Checkbox groupobject or arraychecked={selected[name]} per item
Radio groupsingle valuechecked={value === option} per item
Note: Because checked is derived by comparison rather than stored per-radio, adding or removing a radio option is just adding or removing an array entry — you never have to manage N separate booleans in sync with each other.

Exercise: React Checkbox and Radio

Which prop determines whether a controlled checkbox renders as checked?