Skip to content
FormSubmit

HTML

HTML checkbox in a form: single, multiple, required and styled

How HTML form checkboxes work: consent boxes, multiple checkboxes with one name, unchecked values, required, indeterminate, styling and JavaScript.

5 min readUpdated Oct 9, 2026

An HTML checkbox is <input type="checkbox" name="..." value="...">, and it is submitted only when ticked. Use one on its own for consent, or several with the same name to let people choose multiple options. An unchecked box sends nothing, and a box without a value sends on. Add required to force a tick, and style it with accent-color.

This guide covers single and multiple checkboxes, what actually gets submitted, required and indeterminate states, styling, and reading checkboxes in JavaScript. It's part of the HTML form elements reference.

A checkbox form

newsletter-form.html
<form action="https://formsubmit.app/f/YOUR_FORM_ID" method="POST">
  <label for="email">Email</label>
  <input id="email" type="email" name="email" autocomplete="email" required>

  <fieldset>
    <legend>Topics you'd like to hear about</legend>
    <label><input type="checkbox" name="topics" value="product-updates"> Product updates</label>
    <label><input type="checkbox" name="topics" value="tutorials"> Tutorials</label>
    <label><input type="checkbox" name="topics" value="events"> Events</label>
  </fieldset>

  <label>
    <input type="checkbox" name="consent" value="yes" required>
    I agree to receive emails and can unsubscribe at any time
  </label>

  <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="display:none">
  <button type="submit">Subscribe</button>
</form>

Wrapping each checkbox in its <label> makes the text clickable, which turns a tiny target into a comfortable one. You can also use for and id if the label needs to sit elsewhere.

A single checkbox is a yes/no switch that defaults to "no". It's the right tool for agreeing to terms, opting in to marketing or confirming something:

html
<label>
  <input type="checkbox" name="consent" value="yes" required>
  I agree to the <a href="/privacy">privacy policy</a>
</label>

Two points for consent boxes:

  • Don't pre-tick marketing opt-ins. Under GDPR, a pre-ticked box isn't valid consent. Leave checked off. More in GDPR-compliant contact forms.
  • Give it a meaningful value. consent=yes in your inbox is clearer than consent=on.

Multiple checkboxes with the same name

To let people pick several options, give every checkbox in the group the same name and a different value. If someone ticks "Tutorials" and "Events" in the form above, the browser sends:

text
topics=tutorials&topics=events

The same key appears once for each ticked box. FormSubmit keeps multiple values for the same name, so topics arrives as a list containing both values rather than only the last one.

What about name="topics[]"?

The [] suffix is a PHP convention. PHP only keeps the last value of a repeated key unless the name ends in []. Many tutorials use it for that reason. FormSubmit accepts either form: plain repeated names and names ending in [] both keep every value. If you're not posting to PHP, the plain name is cleaner.

Unchecked checkboxes aren't submitted

An unchecked checkbox is left out of the submission completely. There's no consent=off or consent= with an empty value. On the receiving end, the field simply doesn't exist.

That's usually fine, but if you need an explicit "no", there are two options:

  1. Use a yes/no radio group instead. See HTML radio buttons.
  2. Add a hidden input with the same name before the checkbox:
html
<input type="hidden" name="newsletter" value="no">
<label><input type="checkbox" name="newsletter" value="yes"> Send me the newsletter</label>

When ticked, both values are sent (no then yes), and when not ticked only no is sent. This trick is common with servers that keep the last value. With FormSubmit, both values are kept, so the radio group is the clearer choice.

The default value is "on"

If a checkbox has no value attribute, a ticked box submits the string on:

html
<input type="checkbox" name="urgent">   <!-- submits urgent=on -->

This works but is easy to misread in a spreadsheet or email. Set value to something descriptive.

Required checkboxes

required on a checkbox means that exact box must be ticked before the form submits. It's ideal for consent.

HTML has no "at least one in this group" rule. Putting required on every box in a group would force all of them to be ticked. To require at least one, check the group in JavaScript:

at-least-one.html
<script>
  const form = document.querySelector("form");
  const boxes = form.querySelectorAll('input[name="topics"]');

  function validateTopics() {
    const anyChecked = [...boxes].some((box) => box.checked);
    boxes[0].setCustomValidity(anyChecked ? "" : "Choose at least one topic.");
  }

  boxes.forEach((box) => box.addEventListener("change", validateTopics));
  validateTopics();
</script>

setCustomValidity hooks into the browser's own validation, so the message appears on submit just like a built-in required error.

The indeterminate state

A checkbox can look "partly ticked", shown as a dash. This is useful for a "Select all" box when only some children are ticked. It can only be set with JavaScript:

select-all.html
<fieldset>
  <legend>Services</legend>
  <label><input type="checkbox" id="all-services"> Select all</label>
  <label><input type="checkbox" name="services" value="design"> Design</label>
  <label><input type="checkbox" name="services" value="development"> Development</label>
  <label><input type="checkbox" name="services" value="hosting"> Hosting</label>
</fieldset>

<script>
  const all = document.getElementById("all-services");
  const items = document.querySelectorAll('input[name="services"]');

  all.addEventListener("change", () => {
    items.forEach((item) => { item.checked = all.checked; });
  });

  items.forEach((item) => item.addEventListener("change", () => {
    const ticked = [...items].filter((i) => i.checked).length;
    all.checked = ticked === items.length;
    all.indeterminate = ticked > 0 && ticked < items.length;
  }));
</script>

indeterminate is purely visual. It isn't submitted, and what's sent still depends on checked. The "Select all" box has no name, so it's never submitted itself.

Styling checkboxes

accent-color

accent-color changes the colour of the native checkbox while keeping its accessibility and behaviour:

css
input[type="checkbox"] {
  accent-color: #4f46e5;
  width: 1.125rem;
  height: 1.125rem;
}

A custom checkbox

For a fully custom look, use appearance: none on the real input and draw the tick with a pseudo-element. The input stays focusable, keyboard-operable and submittable:

custom-checkbox.css
input[type="checkbox"] {
  appearance: none;
  width: 1.125rem;
  height: 1.125rem;
  margin: 0;
  border: 2px solid #6b7280;
  border-radius: 0.25rem;
  display: inline-grid;
  place-content: center;
}
input[type="checkbox"]::before {
  content: "";
  width: 0.625rem;
  height: 0.625rem;
  background: #fff;
  clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0, 43% 62%);
  transform: scale(0);
}
input[type="checkbox"]:checked {
  background: #4f46e5;
  border-color: #4f46e5;
}
input[type="checkbox"]:checked::before {
  transform: scale(1);
}
input[type="checkbox"]:focus-visible {
  outline: 2px solid #4f46e5;
  outline-offset: 2px;
}

Don't hide the input with display: none and style a sibling element instead. That breaks keyboard access.

Reading checkboxes in JavaScript

read-checkboxes.js
const form = document.querySelector("form");

// A single checkbox: true or false
const agreed = form.elements.consent.checked;

// All ticked values in a group, as an array
const topics = new FormData(form).getAll("topics");
// e.g. ["tutorials", "events"]

// The same with a selector
const topics2 = [...form.querySelectorAll('input[name="topics"]:checked')]
  .map((box) => box.value);

Use .checked, not .value, to know if a box is ticked. .value returns the value attribute whether the box is ticked or not.

Sending checkbox answers

Checkbox answers arrive as normal fields, with a group's ticked values kept together under one name. Point the form at https://formsubmit.app/f/YOUR_FORM_ID with method="POST" and each submission is stored, spam-filtered and emailed to you. The full setup is in how to send an HTML form to email. For other ways to collect choices, see radio buttons, the select dropdown or the HTML form elements overview.

Frequently asked questions

What value does a checkbox submit?

The value of its value attribute when it is checked. If there is no value attribute, a checked box submits the string on. An unchecked box submits nothing at all.

How do I submit several checkboxes with the same name?

Give them all the same name and different values. The browser sends one name=value pair for each checked box. FormSubmit keeps every value, so they arrive together as a list for that field.

Do I need name="x[]" with square brackets?

Only if your server expects it, which PHP does. FormSubmit accepts both plain repeated names and names ending in [] and keeps multiple values for the same name either way.

How do I make at least one checkbox in a group required?

HTML has no built-in rule for that. Adding required to a checkbox means that specific box must be ticked. For "at least one of these", check the group in JavaScript before submitting and use setCustomValidity to show a message.

How do I change a checkbox's colour?

Set accent-color in CSS, for example accent-color set to your brand colour. For a fully custom design, use appearance none and draw the box and tick yourself while keeping the real input.

Keep reading

Your form backend is 60 seconds away

Sign up with Google or email, create a form, paste the endpoint. Free forever for small sites — no credit card.