Select
A list of options to pick from, opened by a button. No text entry — long lists are reachable by typeahead. Form-associated, with single/multi-select, chips and full keyboard navigation.
Preview
<esa-select label="Fruit" placeholder="Choose a fruit"></esa-select> Select or Combobox?
If the user picks from what you gave them, it is a Select. If they type into it, it is a Combobox.
- Select — a button opens a list. Reach a distant option by
typeahead: type
c,aand it jumps to the first “Ca…” option, the way a native<select>does. The list is never filtered. - Combobox — an input with suggestions. It filters as you type
and can fetch remotely (debounced
searchevent,loading,results-count).
Until 2026-08-15 both components had this backwards: searchable
defaulted to on here, so every select rendered an editable autocomplete,
while Combobox defaulted to a button trigger. Both defaults are now correct and the
old props are deprecated shims that still work and warn.
Sizes
Three sizes, not four. Select has no xs: at 28px the trigger and its chevron fall
below a comfortable tap target, and because the option list it opens is sized independently,
shrinking the trigger buys nothing downstream. sm is the floor — reach for it in
dense table rows and filter bars. Controls without a popup, like
Text Field, still offer xs.
<esa-select label="Small" size="sm"></esa-select>
<esa-select label="Medium" size="md"></esa-select>
<esa-select label="Large" size="lg"></esa-select> States
Multi-select renders chips with chip-mode. Error, required, disabled, and read-only states are all driven by attributes.
<esa-select label="Toppings" multiple chip-mode placeholder="Add toppings"></esa-select>
<esa-select label="Required" required></esa-select>
<esa-select label="Error" error-text="Please make a selection"></esa-select>
<esa-select label="Disabled" disabled></esa-select> API
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | '' | Field label above the control. |
options | EsaOption[] | [] | Selectable options. Set as a property (not an attribute). |
size | 'sm' | 'md' | 'lg' | 'md' | Control size (reflected attribute). No xs — see Sizes. |
placeholder | string | 'Select...' | Placeholder shown when nothing is selected. |
help-text | string | '' | Helper text below the control. Property: `helpText`. |
cue | string | '' | Override the visually-hidden instructional cue read to screen reader users. The default explains how to review options and that typing jumps to a match — which is what makes announcing the list unnecessary as it changes. Do not blank it, or the option list becomes silent with nothing to set expectations. |
error-text | string | '' | Error message; sets the error state when present. Property: `errorText`. |
required | boolean | false | Marks the field required (renders a * after the label). |
live-error | boolean | false | Announce the error the moment it appears rather than only when the field is focused. OFF by default — see the long note on `esa-text-field.liveError`: the house pattern is validate-on-submit with `<esa-error-summary>`, under which a live region per field fires an assertive announcement for EVERY invalid field at once, racing the summary the user was just sent to. Turn it on for fields validated INLINE, on blur. Property: `liveError`. |
disabled | boolean | false | Disables interaction (reflected attribute). |
name | string | undefined | — | Form field name — the key this control submits under. |
multiple | boolean | false | Allow multiple selections. |
searchable | boolean | false | DEPRECATED — a type-to-filter field is an autocomplete, which is <a href='/components/esa-combobox'>esa-combobox</a>. Still honoured; warns at runtime. Typeahead replaces the common use. |
chip-mode | boolean | false | In multi-select, render selected values as removable chips. Property: `chipMode`. |
value | string | string[] | null | — | Property accessor. Single = string; multiple = string array. Set as a JS property — there is no attribute. |
Events
| Event | Type | Default | Description |
|---|---|---|---|
change | CustomEvent<{ value: string | string[] }> | — | Fired on selection change. Composed and bubbles. Form value is the string (single) or comma-joined values (multiple). |
Binding these events from Angular
Every one of these is a CustomEvent, so the payload is on
$event.detail — $event.target.value reads empty, with no
error. The names are lowercase and Angular passes them to
addEventListener verbatim, so camel-casing one means it never fires.
(change)="onChange($event.detail)"
Accessibility
- Input has
role="combobox"witharia-expanded,aria-haspopup="listbox", andaria-autocomplete="list". - Dropdown is
role="listbox"; options arerole="option"witharia-selected/aria-disabled. - Keyboard: ArrowUp/Down navigate, Enter selects, Escape/Tab close. Outside-click closes.
- Chip remove buttons carry an
aria-label("Remove {label}"). - Form-associated via
ElementInternals— value submits with the enclosing form.