Skip to main content

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, and disabled attributes
  • The indeterminate state, and a working "select all"
  • Grouping with <fieldset> and <legend>
  • Styling, accessibility, and common mistakes

The Basics​

index.html
<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.

StateWhat the browser sends
Checkedname=value
UncheckedNothing at all. The name is missing entirely
Checked but disabledNothing
<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 text on. 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."
Missing means "no"

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.

index.html
<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 no value.
  • 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"].

Group with fieldset and legend

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​

AttributeWhat it doesExample
nameThe key sent to the server (shared by a group)name="topping"
valueWhat is sent when checked (default on)value="cheese"
checkedStarts tickedchecked
requiredMust be ticked to submitrequired
disabledCannot be changed, and is not sentdisabled
idConnects a label that uses forid="agree"
autofocusFocus on loadautofocus

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.

No readonly for checkboxes

If 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.

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 required for 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.

index.html
<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>
script.js
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?​

ChoiceUseWhy
CheckboxAny number of options (zero, one, or many), or one yes/noIndependent choices
Radio buttonsExactly one choice from a short listThey exclude each other
SelectOne choice from a long listSaves space
SwitchAn 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:

style.css
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 for and id. 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: Tab moves 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=anything by 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's dir automatically.
  • 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​

  1. Expecting an unchecked box to send something. It sends nothing.
  2. Forgetting the default value is on.
  3. No label, or a label not connected to the box.
  4. Giving every checkbox in a group a different name when you wanted one list.
  5. Using checkboxes for "choose one." Use radio buttons.
  6. Pre-ticking consent boxes.
  7. Bundling several consents in one box.
  8. Using readonly, which has no effect on checkboxes.
  9. Trying to set indeterminate in HTML. It is JavaScript only, and is never sent.
  10. Replacing the real input with a styled <div>.
  11. Tiny boxes and tiny labels that are hard to tap.
  12. Not validating on the server.

Practice Time​

Try these in your editor:

  1. Build one checkbox with a wrapping label. Submit it ticked and unticked, with method="get", and read the address.
  2. Add value="yes", and compare it with the default on.
  3. Add checked, and then untick it and press the form's reset button.
  4. Build a group of three toppings with the same name, and look at the repeated pairs.
  5. Wrap the group in a <fieldset> with a <legend>.
  6. Add a required "I agree" box, and try to submit it unticked.
  7. Use FormData.getAll() to read the group as a list.
  8. Build "select all" with indeterminate.
  9. Style the boxes with accent-color, and make the label a comfortable tap size.
  10. 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=value only when checked. The default value is on. An unchecked box sends nothing.
  • Checkboxes with the same name make a group, and the server receives a list. Wrap them in a fieldset with a legend.
  • checked sets the starting state, and required means the box must be ticked. readonly does nothing.
  • indeterminate is 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.