Combobox
Comboboxes combine a text input with a listbox, allowing users to filter and select from predefined options or enter custom values.
-
Pro Components -
Native Styles -
CSS + Layout Utilities -
Ever-Growing Pattern Library -
Unlimited Hosted Projects -
Pre-Built Pro Themes -
Pro Theme Builder -
Pro Color Tools -
Official Figma Design Kit -
WA Pro Perpetual License -
Actual Human™ Support
This component follows the ARIA APG Combobox pattern and uses live region announcements for result filtering in screen readers.
This component works with standard <form> elements. Please refer to the section on form controls to learn more about form submission and client-side validation.
Examples
Label
Use the label attribute to give the combobox an accessible label. For labels that contain HTML, use the label slot instead.
Hint
Add descriptive hint to a combobox with the hint attribute. For hints that contain HTML, use the hint slot instead.
Placeholder
Use the placeholder attribute to add a placeholder.
Clearable
Use the with-clear attribute to make the control clearable. The clear button only appears when the combobox has a value or text input.
Multiple
To allow multiple options to be selected, use the multiple attribute. Selected options appear as tags and the input filters the list. Pair it with with-clear to reset the selection, and use max-options-visible to cap how many tags show before the rest collapse into a count.
In multiple mode, the text input is used for filtering options only. After selecting an option, the input is cleared so you can continue filtering and selecting more options.
Initial Value
Use the selected attribute on individual options to set the initial selection, similar to native HTML.
For multiple selections, apply it to all selected options.
Framework users can bind directly to the value property for reactive data binding and form state management.
Allowing Custom Values
By default, the combobox only accepts values that match an option. Use allow-custom-value to let users enter arbitrary values.
Grouping Options
Use <wa-divider> to group listbox items visually. You can also use <small> to provide labels, but they won't be announced by most assistive devices.
Appearance
Use the appearance attribute to change the combobox's visual appearance.
Pill
Use the pill attribute to give comboboxes rounded edges.
Size
Use the size attribute to change a combobox's size.
Placement
The preferred placement of the combobox's listbox can be set with the placement attribute. Note that the actual position may vary to ensure the panel remains in the viewport. Valid placements are top and bottom.
Start & End Decorations
Use the start and end slots to add presentational elements like <wa-icon> within the combobox.
Disabled
Use the disabled attribute to disable a combobox.
Creating New Items
Use the allow-create attribute to let users create new options on the fly. When the user types text that doesn't match any existing option, a "Create [value]" option appears at the bottom of the listbox. Selecting it adds a new <wa-option> to the DOM and selects it.
This also works with multiple mode.
For advanced use cases, listen for the wa-create event and call preventDefault() to handle creation yourself. This is useful when you need to normalize values, validate input, or call an API before creating the option.
Custom Filter Function
You can provide a custom filter function to control how options are matched. The function receives the option element and the current query string, and should return true to show the option or false to hide it.
By default, the combobox filters options that contain the query anywhere in the label, but you can customize this to implement fuzzy matching, prefix-only matching, or apply any other filtering logic.
Status Messages
When the listbox has nothing to show, it shows a message in place of the options. Replace any of the defaults with a slot.
| Slot | Shows when | Default text |
|---|---|---|
empty |
There are no options and the user hasn't typed anything | No options |
no-results |
The query matched nothing | No matching results |
loading |
A request is pending and no options are showing yet | Loading |
error |
The last dataSource request failed |
Options could not be loaded |
Any visible option suppresses all four. The loading and error messages only appear when loading options from a server.
Loading Options from a Server
When there are too many options to render up front, the combobox can fetch them as the user types. There are two ways to connect it to your server.
| Data source | Request event | |
|---|---|---|
| Turn it on with | The dataSource property |
The server attribute |
| You provide | A callback that returns the options | <wa-option> elements you render yourself |
| Best for | Most cases | Frameworks that own the option elements |
Either way the combobox stops filtering on the client. Your server decides what matches, so the filter property is ignored.
The rest of this section uses the dataSource property. Set it to a function that returns the options for the user's query, where each returned option needs a value and a label. For the other approach, see Rendering Options Yourself.
combobox.dataSource = async ({ query, signal }) => { const response = await fetch(`/api/countries?q=${encodeURIComponent(query)}`, { signal }); return response.json(); // [{ value: 'ca', label: 'Canada' }, ...] };
The combobox requests options when the listbox opens, using an empty query, and again as the user types. Requests wait for a 250ms pause in typing, which you can change with the filter-debounce attribute, and outdated ones can be canceled with signal.
To start with a value selected, add it as a <wa-option> with the selected attribute, the same as without a data source. Selected options stay selected even when a later response doesn't include them.
<wa-combobox label="Assignee"> <wa-option value="grace" selected>Grace Hopper</wa-option> </wa-combobox>
Setting the value property also works, as long as an option with that value exists. A value with no matching option is dropped, since the combobox only knows the labels of options it has.
<wa-combobox label="Assignee" value="grace"> <wa-option value="grace">Grace Hopper</wa-option> </wa-combobox>
Custom Option Content
To show more than a label, return HTML instead. Use the same <wa-option> markup you'd write by hand, including icons in the start and end slots, <wa-divider>, and group headings. If an option contains text other than its label, such as a description, set the option's label attribute.
Only return HTML you trust.
Unsanitized user input returned from the dataSource callback can introduce XSS vulnerabilities.
Loading and Error Messages
Options that are already showing stay visible while new ones load. When there's nothing to show, the listbox shows a status message instead.
When the dataSource callback throws or rejects, the combobox shows the error message, emits the wa-options-error event, and tries again the next time the user opens the listbox or types. Search for boom below to see a failed request.
Refreshing Options
Call the reload() method to request options again. With the dataSource property it returns a promise that resolves once the new options are in place, which makes it easy to select something you just created on the server. With the server attribute it resolves as soon as wa-options-request is emitted, so wait for your own update before you set the value property.
Rendering Options Yourself
If your framework renders the <wa-option> elements, add the server attribute instead of setting the dataSource property. The combobox emits the wa-options-request event when it needs options and sets the loading property to true. Update the options for event.detail.query, then set the loading property back to false. Skip the update when event.detail.signal.aborted is true, because a newer request has replaced that one.
Custom Tags
When multiple options can be selected, you can provide custom tags by passing a function to the getTag property. Your function can return a string of HTML, a Lit Template, or an HTMLElement. The getTag() function will be called for each option. The first argument is an <wa-option> element and the second argument is the tag's index (its position in the tag list).
Remember that custom tags are rendered in a shadow root. To style them, you can use the style attribute in your template or you can add your own parts and target them with the ::part() selector.
Only pass content you trust to getTag().
Unsanitized user input can introduce XSS vulnerabilities.
When using custom tags with with-remove, you must include the data-value attribute set to the option's value. This allows the combobox to identify which option to deselect when the tag's remove button is clicked.
Accessibility Considerations
Always give the combobox an accessible name with the label attribute or the label slot. Focus stays in the text field while the listbox is open, so the combobox uses a polite live region to announce each option as the user arrows to it, the number of options when the list changes, and any status message shown in place of the options.
If you replace a message using the loading, no-results, empty, or error slot, your text is announced instead. Keep it short, plain text without links or buttons, because screen reader users will hear it but can't interact with it.
API
Importing
If you're using the autoloader or a hosted project, components load on demand — no manual import needed. To cherry-pick a component manually, use one of the following snippets.
Import this component directly from the CDN:
import 'https://ka-f.webawesome.com/[email protected]/components/combobox/combobox.js';
After installing Web Awesome via npm, import this component:
import '@awesome.me/webawesome/dist/components/combobox/combobox.js';
If you're self-hosting Web Awesome, import this component from your server:
import './webawesome/dist/components/combobox/combobox.js';
To import this component for React 18 or below, use the following code:
import WaCombobox from '@awesome.me/webawesome/dist/react/combobox/index.js';
Slots
Learn more about using slots.
| Name | Description |
|---|---|
| (default) | The listbox options. Must be <wa-option> elements. You can use <wa-divider> to group items visually. |
clear-icon
|
An icon to use in lieu of the default clear icon. |
empty
|
Shown in the listbox when there are no options and no query has been typed. |
end
|
An element, such as <wa-icon>, placed at the end of the combobox. |
error
|
Shown in the listbox when the last dataSource request failed. |
expand-icon
|
The icon to show when the control is expanded and collapsed. Rotates on open and close. |
hint
|
Text that describes how to use the input. Alternatively, you can use the hint attribute. |
label
|
The input's label. Alternatively, you can use the label attribute. |
loading
|
Shown in the listbox while options are loading and none are available yet. |
no-results
|
Shown in the listbox when the query matched nothing. |
start
|
An element, such as <wa-icon>, placed at the start of the combobox. |
Attributes & Properties
Learn more about attributes and properties.
| Name | Description | Reflects |
|---|---|---|
allowCreateallow-create |
When true, if the user types text that doesn't match any existing option, a "Create [value]" option appears in the
listbox. Selecting it creates a new
<wa-option> in the DOM and selects it. A cancelable wa-create event fires
before creation.
Type
boolean
Default
false
|
|
allowCustomValueallow-custom-value |
When true, allows the user to enter a value that doesn't match any of the options. Only applies to single-select
comboboxes. When false, the combobox will only accept values that match an option.
Type
boolean
Default
false
|
|
appearanceappearance |
The combobox's visual appearance.
Type
'filled' | 'outlined' | 'filled-outlined'
Default
'outlined'
|
|
autocapitalizeautocapitalize |
Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
Type
'off' | 'none' | 'on' | 'sentences' | 'words' | 'characters'
|
|
autocorrectautocorrect |
Indicates whether the browser's autocorrect feature is on or off. When set as an attribute, use
"off" or "on".
When set as a property, use true or false.
Type
boolean
|
|
currentOption |
The option the user is keying through, or
undefined once it's unusable. In server mode a response or a consumer
swap can remove or hide the highlighted option at any moment, and no reader — Enter above all — may act on it.
Type
WaOption | undefined
|
|
dataSource |
A callback that loads options from a server. It receives the current query and an
AbortSignal and returns the
options to show — an array of { value, label, disabled? } objects, a string of HTML, or an array of elements.
Setting this puts the combobox in server mode, which turns off client-side filtering. HTML is inserted as-is and
is never sanitized, so make sure you trust it.
Type
((request: ComboboxRequest) => Promise<ComboboxOptions> | ComboboxOptions)
| null
Default
null
|
|
disableddisabled |
Disables the combobox control.
Type
boolean
Default
false
|
|
enterkeyhintenterkeyhint |
Used to customize the label or icon of the Enter key on virtual keyboards.
Type
'enter' | 'done' | 'go' | 'next' | 'previous' | 'search' | 'send'
|
|
filter |
A function that customizes how options are filtered based on the input value. The function receives the option
and the current input query string. Return
true to include the option in the filtered list, false to exclude.
By default, options are filtered by checking if the option's label contains the query (case-insensitive).
Ignored in server mode — the server decides what matches.
Type
((option: WaOption, query: string) => boolean) | null
Default
null
|
|
filterDebouncefilter-debounce |
How long to wait, in milliseconds, after the user stops typing before requesting options in server mode. Opening
the listbox and calling
reload() request immediately.
Type
number
Default
250
|
|
form |
By default, form controls are associated with the nearest containing
<form> element. This attribute allows you
to place the form control outside of a form and associate it with the form that has this id. The form must be in
the same document or shadow root for this to work.
Type
HTMLFormElement | null
|
|
getTag |
A function that customizes the tags to be rendered when multiple=true. The first argument is the option, the second
is the current tag's index. The function should return either a Lit TemplateResult or a string containing trusted
HTML of the symbol to render at the specified value.
Type
(option: WaOption, index: number) => TemplateResult | string | HTMLElement
|
|
hinthint |
The combobox's hint. If you need to display HTML, use the
hint slot instead.
Type
string
Default
''
|
|
inputmodeinputmode |
Tells the browser what type of data will be entered by the user, allowing it to display the appropriate virtual
keyboard on supportive devices.
Type
'none' | 'text' | 'decimal' | 'numeric' | 'tel' | 'search' | 'email' | 'url'
|
|
inputValue |
The current text value in the input field.
Type
string
Default
''
|
|
labellabel |
The combobox's label. If you need to display HTML, use the
label slot instead.
Type
string
Default
''
|
|
loadingloading |
Whether a request for options is pending. The combobox sets this to
true the moment a request is scheduled
(including the debounce wait) and, with a dataSource, clears it when the request settles. In event mode, set it
to false yourself once you've updated the options.
Type
boolean
Default
false
|
|
maxOptionsVisiblemax-options-visible |
The maximum number of selected options to show when
multiple is true. After the maximum, "+n" will be shown to
indicate the number of additional items that are selected. Set to 0 to remove the limit.
Type
number
Default
3
|
|
multiplemultiple |
Allows more than one option to be selected.
Type
boolean
Default
false
|
|
namename |
The name of the combobox, submitted as a name/value pair with form data.
Type
string | null
Default
''
|
|
openopen |
Indicates whether or not the combobox is open. You can toggle this attribute to show and hide the menu, or you can
use the
show() and hide() methods and this attribute will reflect the combobox's open state.
Type
boolean
Default
false
|
|
pillpill |
Draws a pill-style combobox with rounded edges.
Type
boolean
Default
false
|
|
placeholderplaceholder |
Placeholder text to show as a hint when the combobox is empty.
Type
string
Default
''
|
|
placementplacement |
The preferred placement of the combobox's menu. Note that the actual placement may vary as needed to keep the
listbox inside of the viewport.
Type
'top' | 'bottom'
Default
'bottom'
|
|
requiredrequired |
The combobox's required attribute.
Type
boolean
Default
false
|
|
serverserver |
Switches the combobox to server mode without a
dataSource callback: client-side filtering is turned off and you
swap the slotted <wa-option> elements yourself in response to wa-options-request, then set loading to
false. Implied when dataSource is set.
Type
boolean
Default
false
|
|
sizesize |
The combobox's size.
Type
'xs' | 's' | 'm' | 'l' | 'xl' | 'small' | 'medium' | 'large'
Default
'm'
|
|
spellcheckspellcheck |
Enables spell checking on the combobox.
Type
boolean
Default
false
|
|
validationTarget |
Where to anchor native constraint validation
Type
undefined | HTMLElement
|
|
validators |
Validators are static because they have
observedAttributes, essentially attributes to "watch"
for changes. Whenever these attributes change, we want to be notified and update the validator.
Type
Validator[]
Default
[]
|
|
valuevalue |
The combobox's value. This will be a string for single select or an array for multi-select.
|
|
withClearwith-clear |
Adds a clear button when the combobox is not empty.
Type
boolean
Default
false
|
|
withHintwith-hint |
Only required for SSR. Set to
true if you're slotting in a hint element so the server-rendered markup
includes the hint before the component hydrates on the client.
Type
boolean
Default
false
|
|
withLabelwith-label |
Only required for SSR. Set to
true if you're slotting in a label element so the server-rendered markup
includes the label before the component hydrates on the client.
Type
boolean
Default
false
|
Methods
Learn more about methods.
| Name | Description | Arguments |
|---|---|---|
blur() |
Removes focus from the control. | |
focus() |
Sets focus on the control. |
options: FocusOptions
|
formStateRestoreCallback() |
Called when the browser is trying to restore element’s state to state in which case reason is "restore", or when the browser is trying to fulfill autofill on behalf of user in which case reason is "autocomplete". In the case of "restore", state is a string, File, or FormData object previously set as the second argument to setFormValue. |
state: string | File | FormData | null,
reason: 'autocomplete' | 'restore'
|
hide() |
Hides the listbox. | |
reload() |
Re-requests options using the current query (or an empty query when the listbox is closed). Resolves once the
response has been applied in dataSource mode, or immediately after wa-options-request is emitted in event
mode, so await combobox.reload() followed by setting value works. |
|
resetValidity() |
Reset validity is a way of removing manual custom errors and native validation. | |
setCustomValidity() |
Do not use this when creating a "Validator". This is intended for end users of components. We track manually defined custom errors so we don't clear them on accident in our validators. |
message: string
|
show() |
Shows the listbox. |
Events
Learn more about events.
| Name | Description |
|---|---|
blur |
Emitted when the control loses focus. |
change |
Emitted when the control's value changes. |
focus |
Emitted when the control gains focus. |
input |
Emitted when the control receives input. |
request |
|
wa-after-hide |
Emitted after the combobox's menu closes and all animations are complete. |
wa-after-show |
Emitted after the combobox's menu opens and all animations are complete. |
wa-clear |
Emitted when the control's value is cleared. |
wa-create |
Emitted when the user selects the "create" option. Call event.preventDefault() to handle creation yourself. The event detail contains { inputValue: string }. |
wa-hide |
Emitted when the combobox's menu closes. |
wa-invalid |
Emitted when the form control has been checked for validity and its constraints aren't satisfied. |
wa-options-error |
Emitted when a dataSource request rejects. The event detail contains { error: unknown, request: { query: string } }. |
wa-options-request |
Emitted in server mode whenever a request for options starts. The event detail contains { query: string, signal: AbortSignal }. |
wa-show |
Emitted when the combobox's menu opens. |
CSS Custom Properties
Learn more about CSS custom properties.
| Name | Description |
|---|---|
--hide-duration |
The duration of the hide animation.
Default
var(--wa-transition-fast)
|
--show-duration |
The duration of the show animation.
Default
var(--wa-transition-fast)
|
--tag-max-size |
When using
multiple, the max size of tags before their content is truncated.
Default
10ch
|
Custom States
Learn more about custom states.
| Name | Description | CSS selector |
|---|---|---|
blank |
The combobox is empty. |
:state(blank)
|
disabled |
The combobox is disabled. |
:state(disabled)
|
loading |
A request for options is pending. |
:state(loading)
|
showing-loading-row |
The open listbox is showing its loading row, so the in-field spinner stays hidden. |
:state(showing-loading-row)
|
CSS Parts
Learn more about CSS parts.
| Name | Description | CSS selector |
|---|---|---|
clear-button |
The clear button. |
::part(clear-button)
|
combobox |
The container the wraps the start, end, value, clear icon, and expand button. |
::part(combobox)
|
combobox-input |
The text input element. |
::part(combobox-input)
|
empty |
The status row shown when there are no options and no query has been typed. |
::part(empty)
|
end |
The container that wraps the end slot. |
::part(end)
|
error |
The status row shown when the last dataSource request failed. |
::part(error)
|
expand-icon |
The container that wraps the expand icon. |
::part(expand-icon)
|
form-control |
The form control that wraps the label, input, and hint. |
::part(form-control)
|
form-control-input |
The combobox's wrapper. |
::part(form-control-input)
|
form-control-label |
The label. |
::part(form-control-label)
|
hint |
The hint's wrapper. |
::part(hint)
|
listbox |
The listbox container where options are slotted. |
::part(listbox)
|
loading |
The status row shown while options are loading and none are available yet. |
::part(loading)
|
no-results |
The status row shown when the query matched nothing. |
::part(no-results)
|
spinner |
The loading spinner shown in the field while options are loading. |
::part(spinner)
|
start |
The container that wraps the start slot. |
::part(start)
|
status |
The listbox status row shown in place of options. Also carries a state-specific part. |
::part(status)
|
tag |
The individual tags that represent each multiselect option. |
::part(tag)
|
tag__content |
The tag's content part. |
::part(tag__content)
|
tag__remove-button |
The tag's remove button. |
::part(tag__remove-button)
|
tag__remove-button__base |
The tag's remove button base part. |
::part(tag__remove-button__base)
|
tags |
The container that houses option tags when multiselect is used. |
::part(tags)
|
label |
Deprecated. Use the form-control-label part instead. |
::part(label)
|
Dependencies
This component automatically imports the following elements. Sub-dependencies, if any exist, will also be included in this list.