HTML Input Type Checkbox
Do you agree to the terms? Do you want the newsletter? Which toppings would you like? A checkbox is the input for yes-or-no questions, and for picking any number of options from a list, including none.
It looks like the easiest input of all. But checkboxes have a famous quirk, and if you do not know it, your forms will quietly lose data: an unchecked checkbox sends nothing at all. In this lesson, you will learn exactly what is sent and when, how to group boxes, how to build "select all", and how to make checkboxes accessible and friendly.
What You Will Learnβ
- How to build a single checkbox, and a group of checkboxes
- What is sent to the server when a box is ticked, and when it is not
- The
value,checked,required, anddisabledattributes - The indeterminate state, and a working "select all"
- Grouping with
<fieldset>and<legend> - Styling, accessibility, and common mistakes
The Basicsβ
- HTML Code
- Browser Output
<label>
<input type="checkbox" name="newsletter" />
Send me the newsletter
</label>
A checkbox is a small square. Click it, and a tick appears. Click it again, and the tick goes away. It has exactly two states: checked and unchecked.
Here the <label> wraps the input, so clicking the text also toggles the box, which gives a much larger click target. (You can instead use for and id, as you learned in Label Element. Either works.)
What Gets Sentβ
This is the part to understand well.
| State | What the browser sends |
|---|---|
| Checked | name=value |
| Unchecked | Nothing at all. The name is missing entirely |
| Checked but disabled | Nothing |
<input type="checkbox" name="newsletter" /> <!-- checked: newsletter=on -->
<input type="checkbox" name="newsletter" value="yes" /> <!-- checked: newsletter=yes -->
- If you give no
value, a checked box sends the texton. That is the default. - If you give a
value, a checked box sends that. - If the box is unchecked, the key does not appear in the data at all. On the server, "not there" means "unchecked."
A server that expects newsletter=false for an unchecked box will never see it. Your server code should treat a missing key as "unchecked," for example newsletter = "newsletter" in form.
If you really need an explicit "false," a common trick is a hidden input with the same name, placed before the checkbox:
<input type="hidden" name="newsletter" value="no" />
<input type="checkbox" name="newsletter" value="yes" />
Unchecked sends newsletter=no. Checked sends both: newsletter=no&newsletter=yes. Most servers keep the last one, but some keep the first, or give a list, so check how your framework behaves.
Try It: See What Is Sentβ
Tick some boxes and press Submit. The demo shows the exact data the browser would send.
- HTML Code
- Browser Output
<form>
<fieldset>
<legend>Which languages do you know?</legend>
<label><input type="checkbox" name="lang" value="html" checked /> HTML</label>
<label><input type="checkbox" name="lang" value="css" /> CSS</label>
<label><input type="checkbox" name="lang" value="js" /> JavaScript</label>
<label><input type="checkbox" name="newsletter" /> Send me the newsletter</label>
</fieldset>
<button type="submit">Submit</button>
</form>
Try these:
- Submit with only HTML ticked:
lang=html. - Tick all three languages:
lang=html&lang=css&lang=js. The same name repeats, once for each ticked box. - Tick "Send me the newsletter": it sends
newsletter=on, since there is novalue. - Untick everything: nothing is sent.
Checkbox Groupsβ
A group is just several checkboxes that share the same name. Each has its own value.
<fieldset>
<legend>Choose your toppings</legend>
<label><input type="checkbox" name="topping" value="cheese" /> Cheese</label>
<label><input type="checkbox" name="topping" value="mushroom" /> Mushrooms</label>
<label><input type="checkbox" name="topping" value="olives" /> Olives</label>
</fieldset>
On the server, you receive a list: topping=cheese&topping=olives. How you read the list depends on your language. For example, in JavaScript, new FormData(form).getAll("topping") returns ["cheese", "olives"].
A <fieldset> with a <legend> tells screen readers the question the boxes answer: "Choose your toppings, group. Cheese, checkbox, not checked." Without it, a listener hears "Cheese" and has no idea what it belongs to. You will study this in Fieldset and Legend.
A group where you can pick one only is not for checkboxes. That is a job for radio buttons.
Attributes for Checkboxesβ
| Attribute | What it does | Example |
|---|---|---|
name | The key sent to the server (shared by a group) | name="topping" |
value | What is sent when checked (default on) | value="cheese" |
checked | Starts ticked | checked |
required | Must be ticked to submit | required |
disabled | Cannot be changed, and is not sent | disabled |
id | Connects a label that uses for | id="agree" |
autofocus | Focus on load | autofocus |
checked is a boolean attribute: its presence means "yes." It sets the starting state only. After the visitor clicks, the state changes, but the attribute does not. In JavaScript, read input.checked for the current state, and input.defaultChecked for the starting one.
Attributes that do not apply to checkboxes: readonly (it has no effect on a checkbox, and many people expect it to), placeholder, min, max, step, pattern, maxlength, and size.
readonly for checkboxesIf you want a box that shows a state but cannot be changed, disabled is the only built-in way, and a disabled box is not sent. To show a fixed state and send it, use a disabled checkbox together with a hidden input that carries the value.
Required Checkboxes and Consentβ
On a checkbox, required means "this box must be ticked." It is perfect for "I agree to the terms."
<label>
<input type="checkbox" name="terms" required />
I agree to the <a href="/terms">terms of service</a>
</label>
If the box is unticked, the browser blocks the form with a message such as "Please check this box if you want to proceed."
Good practice for consent boxes:
- Never pre-tick them. Consent that is "ticked for you" is often not valid consent, and many laws (such as GDPR in Europe) forbid it. The visitor must do it on purpose.
- Keep each consent separate. Do not bundle "I agree to the terms and the marketing emails" in one box.
- Put the link text inside the label, so it is clear what is being agreed to. A link inside the label still works. Clicking the link opens it, and does not toggle the box.
- Do not use
requiredfor optional things such as newsletters.
The Indeterminate State and Select Allβ
A checkbox can look like a third state: a dash instead of a tick. It is called indeterminate, and it means "some, but not all." It is used on a "select all" box above a group.
Two facts you must know:
- It is a visual state only. It can only be set in JavaScript (
input.indeterminate = true), and there is no HTML attribute for it. - It does not change
checked, and it is not sent. A form never sends "indeterminate."
Try the demo. Tick some of the boxes, and watch the "Select all" box change.
- HTML Code
- Browser Output
<fieldset>
<legend>Notifications</legend>
<label><input type="checkbox" id="all" /> <strong>Select all</strong></label>
<label><input type="checkbox" name="notify" value="email" checked /> Email updates</label>
<label><input type="checkbox" name="notify" value="sms" /> SMS alerts</label>
<label><input type="checkbox" name="notify" value="digest" /> Weekly digest</label>
</fieldset>
<p id="count" role="status"></p>
const master = document.getElementById("all");
const boxes = document.querySelectorAll('input[name="notify"]');
const count = document.getElementById("count");
function refresh() {
const checked = [...boxes].filter((box) => box.checked).length;
master.checked = checked === boxes.length;
master.indeterminate = checked > 0 && checked < boxes.length;
count.textContent = checked + " of " + boxes.length + " selected";
}
master.addEventListener("change", () => {
boxes.forEach((box) => (box.checked = master.checked));
refresh();
});
boxes.forEach((box) => box.addEventListener("change", refresh));
refresh();
How it works:
- When any item changes, we count how many are ticked.
- All ticked: the master is checked. Some ticked: the master is indeterminate. None: it is plain.
- Clicking the master ticks or unticks every item.
- The count in a
role="status"region is announced to screen reader users without moving focus. - The master box has no
name, so it is not sent. Only the real items are.
Checkbox, Radio, or Switch?β
| Choice | Use | Why |
|---|---|---|
| Checkbox | Any number of options (zero, one, or many), or one yes/no | Independent choices |
| Radio buttons | Exactly one choice from a short list | They exclude each other |
| Select | One choice from a long list | Saves space |
| Switch | An instant on/off setting (like "Dark mode") | Looks like a toggle, and applies at once |
A "switch" is usually a checkbox with different styling and role="switch". Use it for settings that take effect immediately, and a normal checkbox for answers you submit with a form.
<label>
<input type="checkbox" role="switch" name="dark" />
Dark mode
</label>
Styling Checkboxesβ
The easiest, safest way to change the colour is accent-color. It keeps the native box, with all its accessibility:
input[type="checkbox"] {
accent-color: #2563eb; /* the tick and fill colour */
width: 1.25rem;
height: 1.25rem;
cursor: pointer;
}
input[type="checkbox"]:focus-visible {
outline: 3px solid #2563eb;
outline-offset: 2px;
}
label {
display: inline-flex;
align-items: center;
gap: 0.5rem;
min-height: 2.75rem; /* easy to tap */
}
You can also react to the state with CSS:
input[type="checkbox"]:checked + span {
font-weight: 600;
}
input[type="checkbox"]:indeterminate {
accent-color: #64748b;
}
If you must build a custom look (with appearance: none), keep the real <input> in place, so the keyboard, screen readers, and form behaviour all work, and draw the box and tick with CSS. Never replace it with a <div> that only looks like a checkbox.
Accessibility at a Glanceβ
- Every checkbox needs a label. Wrap the input, or use
forandid. The label text must say what ticking the box means. - Make the label part of the click target, which helps people with limited fine motor control, and everyone on a phone.
- Group related boxes with
<fieldset>and<legend>. - Keyboard:
Tabmoves between boxes, and the Space bar toggles the focused one. This works for free with real checkboxes. - Keep a visible focus outline, and a size of at least about 24 pixels (44 pixels of tap area is better).
- Do not rely on colour alone to show checked or unchecked. The tick shape does that.
- Write labels as positive statements: "Send me the newsletter," not "Do not send me the newsletter," which makes ticking confusing.
- Say what is required, and keep error messages clear: "You must accept the terms to continue."
- Do not change the state of one checkbox on another's change without telling the visitor, except in an obvious pattern such as "select all."
Security and Validation Notesβ
- Re-check on the server. Anyone can send any data. Treat a checkbox as "key present or not," and check every value against the list you expect. A visitor can add
topping=anythingby hand. - Never trust a hidden or disabled value to decide permissions. Such things are visible and changeable.
- Check required consent on the server, and store when and what the visitor agreed to.
- Use allow-lists: for a group, accept only the values you defined.
See Secure Form Design.
Global Audience Notesβ
- Write short, plain labels that translate easily, and avoid double negatives, which are especially hard in a second language.
- Right-to-left languages place the box on the right, and the text on its left. Real checkboxes and
<label>follow the page'sdirautomatically. - Consent laws differ by region. Pre-ticked boxes and bundled consent are not allowed in many places.
- Do not assume a tick means the same everywhere. In some cultures, a tick or a cross can mean "correct." The text of the label removes any doubt.
Common Mistakes to Avoidβ
- Expecting an unchecked box to send something. It sends nothing.
- Forgetting the default
valueison. - No label, or a label not connected to the box.
- Giving every checkbox in a group a different
namewhen you wanted one list. - Using checkboxes for "choose one." Use radio buttons.
- Pre-ticking consent boxes.
- Bundling several consents in one box.
- Using
readonly, which has no effect on checkboxes. - Trying to set
indeterminatein HTML. It is JavaScript only, and is never sent. - Replacing the real input with a styled
<div>. - Tiny boxes and tiny labels that are hard to tap.
- Not validating on the server.
Practice Timeβ
Try these in your editor:
- Build one checkbox with a wrapping label. Submit it ticked and unticked, with
method="get", and read the address. - Add
value="yes", and compare it with the defaulton. - Add
checked, and then untick it and press the form's reset button. - Build a group of three toppings with the same name, and look at the repeated pairs.
- Wrap the group in a
<fieldset>with a<legend>. - Add a required "I agree" box, and try to submit it unticked.
- Use
FormData.getAll()to read the group as a list. - Build "select all" with
indeterminate. - Style the boxes with
accent-color, and make the label a comfortable tap size. - Bonus: build the hidden-input trick, and see what your server framework does with two values.
Quick Recapβ
Click a question to check your answer.
1. What is sent when a checkbox is unchecked?
Nothing. The name does not appear in the data at all. On the server, a missing key means unchecked.
2. What is sent when a checked checkbox has no value?
The text on, as in newsletter=on. With a value attribute, it sends that value instead.
3. How do you make a group of checkboxes?
Give them the same name and different values. The browser sends one pair for each ticked box, such as topping=cheese&topping=olives. Group them in a <fieldset> with a <legend>.
4. What does required mean on a checkbox?
The box must be ticked before the form can be submitted. It suits "I agree to the terms," but should not be used for optional things.
5. What is the indeterminate state, and is it sent?
A dash that shows "some, but not all" on a select-all box. It can only be set in JavaScript, and it is a visual state only. It is not sent, and does not change checked.
6. When should you use radio buttons instead of checkboxes?
When the visitor must choose exactly one option from a short list. Checkboxes are for any number of choices, or a single yes or no.
Key Takeawaysβ
- A checkbox has two states, and sends
name=valueonly when checked. The default value ison. An unchecked box sends nothing. - Checkboxes with the same name make a group, and the server receives a list. Wrap them in a
fieldsetwith alegend. checkedsets the starting state, andrequiredmeans the box must be ticked.readonlydoes nothing.indeterminateis JavaScript-only, visual, and never sent. It is the key to a good "select all."- Never pre-tick consent, keep each consent separate, and write labels as clear positive statements.
- Style with
accent-color, keep the real input, make labels big to tap, and always validate on the server.
Checkboxes let you pick many. Next, learn how to pick exactly one, in Radio, or return to the Forms overview.