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;
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
Exercise: React Checkbox and Radio
Which prop determines whether a controlled checkbox renders as checked?