Checkboxes and multi-select

Receive every ticked checkbox and every selected option from an HTML form, as a list in JSON and webhooks and as a, b, c in email.

Checkbox groups and multi-select lists send several values under one name. SilentForms keeps every one of them, in the order the browser sent them, so a visitor who ticks three boxes shows up with three answers, not just the last one.

Checkbox groups#

Give every checkbox in the group the same name and a distinct value:

<form action="https://silentforms.com/api/submit" method="POST">
  <input type="hidden" name="accessKey" value="YOUR_ACCESS_KEY_HERE">

  <label for="email">Email</label>
  <input id="email" type="email" name="email" required>

  <fieldset>
    <legend>What are you interested in?</legend>
    <label><input type="checkbox" name="interests" value="Design"> Design</label>
    <label><input type="checkbox" name="interests" value="Development"> Development</label>
    <label><input type="checkbox" name="interests" value="Hosting"> Hosting</label>
  </fieldset>

  <input type="checkbox" name="botcheck" style="display: none;" tabindex="-1" autocomplete="off">
  <button type="submit">Send</button>
</form>

A <fieldset> with a <legend> tells screen readers that the boxes belong to one question.

Ticking Design and Hosting sends interests=Design&interests=Hosting, which arrives as the list ["Design", "Hosting"]. Unticked boxes are not sent at all, so a group with nothing ticked does not appear in the submission. Without a value attribute, a ticked box sends the word on, which is rarely what you want.

The name[] convention#

With repeated names, a field becomes a list only when more than one value arrives: ticking just Design gives the plain value "Design". If something downstream (a spreadsheet script, your own API) expects the same shape every time, end the name with []:

<label><input type="checkbox" name="interests[]" value="Design"> Design</label>
<label><input type="checkbox" name="interests[]" value="Development"> Development</label>
<label><input type="checkbox" name="interests[]" value="Hosting"> Hosting</label>

A name ending in [] is always a list, even with one box ticked, and arrives without the brackets: the field is called interests. This is the convention PHP and Formspree use, so forms written for them keep working.

A single checkbox#

A lone checkbox, such as a newsletter opt-in, is one value:

<label>
  <input type="checkbox" name="newsletter" value="yes">
  Send me the monthly newsletter
</label>

It arrives as newsletter: yes when ticked and is missing when not. If you need an explicit yes or no in every submission, use two radio buttons instead (see below).

Select multiple#

A <select multiple> behaves exactly like a checkbox group: each selected option is one value.

<label for="services">Services (hold Ctrl or Cmd to pick several)</label>
<select id="services" name="services[]" multiple size="4">
  <option>Logo design</option>
  <option>Website</option>
  <option>Copywriting</option>
  <option>Photography</option>
</select>

Multi-select lists are awkward on desktop for people who do not know the Ctrl/Cmd trick. A checkbox group is usually easier to use.

Radio buttons#

Radio buttons share a name too, but the browser only ever sends the one that is selected, so they always arrive as a single value:

<fieldset>
  <legend>Preferred contact method</legend>
  <label><input type="radio" name="contact_method" value="Email" checked> Email</label>
  <label><input type="radio" name="contact_method" value="Phone"> Phone</label>
</fieldset>

Do not add [] to radio names: you would get a list with one item instead of a plain value.

Sending JSON from JavaScript#

Object.fromEntries(new FormData(form)) keeps only the last value of each name, so it silently drops all but one ticked box. Collect repeated names into arrays instead:

function formToObject(form) {
  const data = {};
  for (const [rawKey, value] of new FormData(form)) {
    const isList = rawKey.endsWith('[]');
    const key = isList ? rawKey.slice(0, -2) : rawKey;
    if (key in data) {
      data[key] = [].concat(data[key], value);
    } else {
      data[key] = isList ? [value] : value;
    }
  }
  return data;
}

const data = formToObject(form);
// { email: "[email protected]", interests: ["Design", "Hosting"], ... }

A JSON body can also send arrays directly, for example { "interests": ["Design", "Hosting"] }. Up to 100 values per field are kept.

How the values look#

Whereinterests with Design and Hosting ticked
Notification emailDesign, Hosting
Webhook payload"interests": ["Design", "Hosting"]
Dashboard submissionsDesign, Hosting
CSV exportOne cell: Design, Hosting

Webhooks get a real JSON array, so automation tools can loop over the values or join them however they like. See Webhooks.

Next steps#