HyMultiCombobox
Usage examples on this page are written for Lit / plain HTML (
<hy-multi-combobox>). The same component ships asHyMultiComboboxin@whitespaceux/harmony-react(native React 19), with typed wrappers for Angular, Solid, Svelte, and Vue. The API reference below applies to all frameworks.
A multi-combobox — a filterable multi-select that combines a text input with a listbox popup and a row of selected-value chips. Sibling to hy-combobox; a separate element rather than a multiple flag because multi-select carries a different internal model (array value, chip rendering, toggle-on-Enter keyboard semantics, multi-entry form participation) that would otherwise spread conditionals throughout the single-select implementation.
Follows the WAI-ARIA APG combobox pattern: DOM focus stays on the input, option highlight moves via aria-activedescendant. Enter toggles the highlighted option without closing the listbox; Backspace on an empty input removes the last selected chip.
The listbox is positioned with Floating UI: offset + flip + shift keep the popup inside the viewport near the input, and the size middleware caps the listbox height to the available space. autoUpdate repositions on scroll/resize while the listbox is open.
Selected values render as dismissible <hy-chip> elements ahead of the text input. Chips wrap to multiple rows when the input row fills; long labels truncate with ellipsis. An optional maxSelections caps the count — once reached, unselected options appear disabled in the listbox.
Options are passed as a property (.options = [...]). The values property is the currently committed option values (empty array when none). Filtering is controlled internally via filterOption; return false for every option to drive results server-side.
Accessible name. Provide one of: the label property, aria-label on the host, or aria-labelledby referencing another element. The dev-mode WCAG 1.3.1 warning fires only when none is set; aria-label / aria-labelledby on the host are forwarded to the inner <input>.
Examples
<hy-multi-combobox label="Countries" placeholder="Search countries"></hy-multi-combobox>
<script>
const box = document.querySelector('hy-multi-combobox');
box.options = [
{ value: 'ch', label: 'Switzerland' },
{ value: 'fr', label: 'France' },
{ value: 'de', label: 'Germany' },
];
box.addEventListener('change', (e) => console.log(e.detail.values));
</script>API
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
options | — | MultiComboboxOption[] | [] | Option list. Property-only — arrays are not reflectable. |
values | — | string[] | [] | Committed values in selection order. Property-only — arrays are not reflectable. |
label | label | string | '' | Visible label above the input. |
helperText | helper-text | string | '' | Helper text below the input. Hidden when errorMessage is shown. |
errorMessage | error-message | string | '' | Error message — shown below the input when invalid. |
placeholder | placeholder | string | '' | Placeholder shown when the input is empty and no chips are selected. |
size | size | 'small' | 'medium' | 'large' | 'medium' | Input size — matches sibling form controls. |
layout | layout | 'block' | 'inline' | 'block' | Row layout: block (default) stacks label above the combobox; inline puts label on the left and combobox on the right. Helper/error always sit under the control (col 2–end), never under the label. Set --hy-multi-combobox-label-column-width on a wrapping container so stacked comboboxes align. |
width | width | string | undefined | — | Width of the combobox container. Accepts any valid CSS width value (e.g. '400px', '100%', '20rem'). The host fills its container by default; set width to constrain the field. Mirrors hy-text-input. |
name | name | string | '' | Form name — surfaces the selected values to FormData as repeated entries. |
disabled | disabled | boolean | false | Disables the control. |
required | required | boolean | false | Marks the field as required for form validation (at least one selection). |
invalid | invalid | boolean | false | Visually marks the field as invalid. |
clearable | clearable | boolean | true | Whether to show a clear-all button when at least one value is set. |
emptyText | empty-text | string | 'No results' | Fallback text when no options match the current query. |
open | open | boolean | false | Controlled open state. Leave unset for uncontrolled behavior. |
maxSelections | max-selections | number | 0 | Maximum number of selections. When reached, unselected options render as disabled in the listbox. 0 or unset means unlimited. |
autofocus | autofocus | boolean | false | Marks this multi-combobox as the auto-focus target. Read by hy-dialog (and other overlay containers) on show to choose initial focus. The marker on the host applies even though the inner typeahead input is what actually receives focus. Also focuses on first connect when used outside an overlay, mirroring native HTML autofocus but routed through JS so SSR'd HTML doesn't pull focus during hydration. |
autoselect | autoselect | boolean | false | After auto-focus lands, selects the typeahead input's current text (typically empty). Pairs with autofocus for symmetry with the single-select combobox. |
filterOption | — | MultiComboboxFilter | defaultFilter | Custom filter. Called for each option; return true to include. Default is a case-insensitive substring match on label. |
form | — | HTMLFormElement | null | — | — |
validity | — | ValidityState | — | — |
validationMessage | — | string | — | — |
Events
| Event | Detail | Description |
|---|---|---|
change | — | Fires when the selection changes. Detail: MultiComboboxChangeDetail. |
input | — | Fires while the user types. Detail: MultiComboboxInputDetail. |
show | — | Fires when the listbox opens (does NOT bubble). Detail: HyVisibilityDetail. |
hide | — | Fires when the listbox closes (does NOT bubble). Detail: HyVisibilityDetail. |
CSS Parts
| Part | Description |
|---|---|
base | Outer wrapper containing label, control, helper/error. |
label | The <label> element. |
container | The control row holding chips, input, and trailing icons. |
chips | Wrapper around selected-value chips. |
chip | An individual selected-value chip. |
input | The text <input>. |
clear-button | The clear affordance (clears all chips when set). |
expand-icon | The chevron indicating popup state. |
listbox | The popup <ul role="listbox">. |
option | An individual <li role="option">. |
option-check | The inline check mark on selected options. |
empty | The no-results message container. |
helper-text | Helper text below the input. |
error-message | Error message below the input. |
CSS Custom Properties
| Property | Description |
|---|---|
--hy-multi-combobox-listbox-max-height | Maximum height of the popup before scrolling. Overridden at runtime by Floating UI's size middleware when viewport space is tighter. |
--hy-multi-combobox-label-column-width | Width of the label column in layout="inline" mode. No default — set on a wrapping container so stacked comboboxes align. |
--hy-multi-combobox-height-sm | Outer height for small size variant. Per-component override in the form-control height contract (see .claude/rules/control-heights.md). Multi-combobox is the only form control that applies this as min-height rather than height — chips can wrap to multiple rows above the floor. Default: var(--hy-control-height-sm) |
--hy-multi-combobox-height-md | Outer height for medium size variant. Default: var(--hy-control-height-md) |
--hy-multi-combobox-height-lg | Outer height for large size variant. Default: var(--hy-control-height-lg) |
--hy-multi-combobox-hover-duration | Transition duration for hover and focus state feedback |
--hy-multi-combobox-hover-easing | Transition easing for hover and focus state feedback |
--hy-multi-combobox-expand-duration | Transition duration for the listbox expand and collapse animation |
--hy-multi-combobox-expand-easing | Transition easing for the listbox expand and collapse animation |
Methods
select()
Selects the typeahead input's current text. The chip values are not affected — they are not text content.