Free-form text is powerful but unpredictable — ask for a "country" as a plain textfield and you'll get "USA," "United States," "U.S.A.," and every other spelling a user can invent. select solves this by constraining input to a fixed, known list, turning messy free text into clean, predictable data you can actually query against later.
What you'll learn in this lesson
- The
#optionsarray and how its keys become the stored value - Enabling multiple selection with
#multiple - Adding a genuinely empty starting state with
#empty_option
A basic select
$form['country'] = [
'#type' => 'select',
'#title' => $this->t('Country'),
'#options' => [
'us' => $this->t('United States'),
'ca' => $this->t('Canada'),
'mx' => $this->t('Mexico'),
],
];
The crucial detail: #options is a keyed array, and it's the key ('us', 'ca', 'mx') that gets stored as the submitted value, not the human-readable label. This is what makes select data reliably queryable later — you're always working with a small, fixed set of machine-safe keys, regardless of how the label text gets translated.
Allowing multiple selections
$form['tags']['#multiple'] = TRUE;
With #multiple => TRUE, the browser renders a multi-select list box instead of a dropdown, and $form_state->getValue('tags') returns an array of selected keys instead of a single scalar. Always check whether your submit handler expects a string or an array before flipping this on — it changes the submitted value's shape entirely.
Forcing a genuine empty state
$form['country']['#empty_option'] = $this->t('- Select a country -');
Without #empty_option, a select with no explicit default silently pre-selects its first real option — meaning a user who never touches the dropdown submits a country they never actually chose. #empty_option adds a genuinely blank, unselectable-as-valid placeholder at the top, so "nothing selected" is a real, distinguishable state your validation can actually check for.
#empty_option and without an explicit #default_value will submit its first option's value even if the user never interacted with the field at all — indistinguishable, from the data's perspective, from a deliberate choice. Always add #empty_option on any select where "the user didn't pick anything" is a meaningfully different outcome from "the user picked the first item."Quick check: a select has three options and no
#empty_option, and a user submits the form without touching the dropdown at all. What value gets submitted? (The first option's key — silently, as if the user had deliberately chosen it. This is exactly why#empty_optionmatters whenever "no selection" needs to be a real, checkable state.)
Key takeaways
#optionskeys are what gets stored — always use stable, machine-safe keys, translate only the visible labels.#multiple => TRUEchanges the submitted value from a single string to an array — update your submit handler accordingly.- Add
#empty_optionwhenever "nothing selected" needs to be distinguishable from "the first option was chosen" — without it, the first option is silently the default.
Coming up next
Select handles "choose from a list." For the simplest possible binary choice — a single yes/no toggle — the next lesson covers the checkbox element.