Using the Checkbox Element for Single Boolean Choicesfor Drupal 11 , 10 , 9 , and 8

Last updated :  

A single checkbox is the simplest form element there is — one box, checked or not. Simple as it looks, its submitted value works a little differently from every other element type, and that difference is worth understanding before it surprises you in a validation function.

What you'll learn in this lesson

  • The basic checkbox definition
  • Exactly what value gets submitted when checked versus unchecked
  • Setting a checked-by-default state with #default_value

A basic checkbox

$form['agree_terms'] = [
  '#type' => 'checkbox',
  '#title' => $this->t('I agree to the terms and conditions'),
  '#required' => TRUE,
];

Notice the title here reads as a full statement rather than a short label — that's the convention for a single checkbox specifically, since the checkbox and its title together form one sentence a user reads and agrees (or doesn't agree) to.

What actually gets submitted

This is the detail that catches people off guard: a checkbox's submitted value is always either 1 (checked) or 0 (unchecked) — never a boolean TRUE/FALSE, and critically, never absent. Unlike a raw HTML checkbox (where an unchecked box simply isn't included in the submitted form data at all), Drupal's Form API guarantees the key always exists in $form_state->getValues(), set to 0 when unchecked. This is deliberate — it means $form_state->getValue('agree_terms') is always safe to read directly, with no need for an isset() check first.

if ($form_state->getValue('agree_terms')) {
  // Checked — value is 1 here, not TRUE.
}

Setting the default checked state

'#default_value' => $config->get('feature_enabled') ? 1 : 0,

Use 1/0 (or any truthy/falsy PHP value) here — Drupal coerces it correctly either way, but writing it explicitly as 1/0 keeps it consistent with what actually comes back from the submitted form.

PHP's loose comparison can bite you here: $form_state->getValue('agree_terms') == FALSE technically works because PHP coerces 0 == FALSE to true, but relying on loose comparison habits with form values is a good way to introduce a subtle bug later when you're less careful. Prefer a direct truthy check (if (!$form_state->getValue('agree_terms'))) or an explicit === 0 when you genuinely need strictness.

Quick check: a user submits a form leaving an optional checkbox unchecked. Is the key present at all in $form_state->getValues()? (Yes — set to 0. This is different from a raw HTML form, where an unchecked checkbox is simply omitted from the POST data entirely. Drupal's Form API always includes the key.)

Key takeaways

  • A checkbox's submitted value is always 1 or 0 — never absent, unlike a raw HTML checkbox.
  • Because the key is always present, you never need an isset() guard before reading a checkbox's value.
  • Phrase a single checkbox's #title as a complete statement — box and label together read as one sentence.

Coming up next

One checkbox handles a single yes/no. The next lesson covers checkboxes (plural) — letting a user select any number of options from a list.