Combobox
<cw-combobox> | CwCombobox
Comboboxes combine a text input with a listbox, allowing users to filter a list of options by typing.
<cw-combobox label="Search fruits"> <cw-option value="apple">Apple</cw-option> <cw-option value="apricot">Apricot</cw-option> <cw-option value="banana">Banana</cw-option> <cw-option value="blueberry">Blueberry</cw-option> <cw-option value="cherry">Cherry</cw-option> <cw-option value="grape">Grape</cw-option> <cw-option value="mango">Mango</cw-option> <cw-option value="orange">Orange</cw-option> <cw-option value="peach">Peach</cw-option> <cw-option value="pear">Pear</cw-option> </cw-combobox>
import CwCombobox from '@cordwainer/cw-elements/dist/react/combobox'; import CwOption from '@cordwainer/cw-elements/dist/react/option'; const App = () => ( <CwCombobox label="Search fruits"> <CwOption value="apple">Apple</CwOption> <CwOption value="apricot">Apricot</CwOption> <CwOption value="banana">Banana</CwOption> <CwOption value="blueberry">Blueberry</CwOption> <CwOption value="cherry">Cherry</CwOption> <CwOption value="grape">Grape</CwOption> <CwOption value="mango">Mango</CwOption> <CwOption value="orange">Orange</CwOption> <CwOption value="peach">Peach</CwOption> <CwOption value="pear">Pear</CwOption> </CwCombobox> );
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
Labels & Help Text
Use the label attribute to give the combobox an accessible label and help-text to
add descriptive text. For content that contains HTML, use the label and
help-text slots instead.
<cw-combobox label="Assignee" help-text="Start typing to filter the list of teammates."> <cw-option value="alex">Alex Johnson</cw-option> <cw-option value="bailey">Bailey Smith</cw-option> <cw-option value="casey">Casey Lee</cw-option> <cw-option value="drew">Drew Patel</cw-option> </cw-combobox>
import CwCombobox from '@cordwainer/cw-elements/dist/react/combobox'; import CwOption from '@cordwainer/cw-elements/dist/react/option'; const App = () => ( <CwCombobox label="Assignee" help-text="Start typing to filter the list of teammates."> <CwOption value="alex">Alex Johnson</CwOption> <CwOption value="bailey">Bailey Smith</CwOption> <CwOption value="casey">Casey Lee</CwOption> <CwOption value="drew">Drew Patel</CwOption> </CwCombobox> );
Placeholders
Use the placeholder attribute to add a placeholder.
<cw-combobox placeholder="Search..."> <cw-option value="option-1">Option 1</cw-option> <cw-option value="option-2">Option 2</cw-option> <cw-option value="option-3">Option 3</cw-option> </cw-combobox>
import CwCombobox from '@cordwainer/cw-elements/dist/react/combobox'; import CwOption from '@cordwainer/cw-elements/dist/react/option'; const App = () => ( <CwCombobox placeholder="Search..."> <CwOption value="option-1">Option 1</CwOption> <CwOption value="option-2">Option 2</CwOption> <CwOption value="option-3">Option 3</CwOption> </CwCombobox> );
Clearable
Use the clearable attribute to make the control clearable. The clear button only appears when
the combobox has a value.
<cw-combobox value="option-1" clearable> <cw-option value="option-1">Option 1</cw-option> <cw-option value="option-2">Option 2</cw-option> <cw-option value="option-3">Option 3</cw-option> </cw-combobox>
import CwCombobox from '@cordwainer/cw-elements/dist/react/combobox'; import CwOption from '@cordwainer/cw-elements/dist/react/option'; const App = () => ( <CwCombobox value="option-1" clearable> <CwOption value="option-1">Option 1</CwOption> <CwOption value="option-2">Option 2</CwOption> <CwOption value="option-3">Option 3</CwOption> </CwCombobox> );
Disabled
Use the disabled attribute to disable a combobox.
<cw-combobox placeholder="Disabled" disabled> <cw-option value="option-1">Option 1</cw-option> <cw-option value="option-2">Option 2</cw-option> <cw-option value="option-3">Option 3</cw-option> </cw-combobox>
import CwCombobox from '@cordwainer/cw-elements/dist/react/combobox'; import CwOption from '@cordwainer/cw-elements/dist/react/option'; const App = () => ( <CwCombobox placeholder="Disabled" disabled> <CwOption value="option-1">Option 1</CwOption> <CwOption value="option-2">Option 2</CwOption> <CwOption value="option-3">Option 3</CwOption> </CwCombobox> );
Allowing Custom Values
By default, a combobox only accepts values that match one of its <cw-option> elements —
if the user types something else and moves on, the input reverts to the previous selection. Add the
allow-custom-value attribute to let users enter free-text values that don’t match any option.
This is only supported in single-select mode.
<cw-combobox label="Tag" allow-custom-value help-text="Pick an existing tag or type a new one."> <cw-option value="bug">Bug</cw-option> <cw-option value="enhancement">Enhancement</cw-option> <cw-option value="documentation">Documentation</cw-option> </cw-combobox>
import CwCombobox from '@cordwainer/cw-elements/dist/react/combobox'; import CwOption from '@cordwainer/cw-elements/dist/react/option'; const App = () => ( <CwCombobox label="Tag" allow-custom-value help-text="Pick an existing tag or type a new one."> <CwOption value="bug">Bug</CwOption> <CwOption value="enhancement">Enhancement</CwOption> <CwOption value="documentation">Documentation</CwOption> </CwCombobox> );
Multiple
To allow more than one option to be selected, use the multiple attribute. Selected options are
displayed as removable <cw-tag> chips alongside the input, which remains usable for
filtering the remaining options. It’s a good practice to use clearable when this option is
enabled. To set multiple values at once, set value to a space-delimited list of values.
<cw-combobox label="Skills" value="javascript css" multiple clearable> <cw-option value="html">HTML</cw-option> <cw-option value="css">CSS</cw-option> <cw-option value="javascript">JavaScript</cw-option> <cw-option value="typescript">TypeScript</cw-option> <cw-option value="python">Python</cw-option> <cw-option value="go">Go</cw-option> </cw-combobox>
import CwCombobox from '@cordwainer/cw-elements/dist/react/combobox'; import CwOption from '@cordwainer/cw-elements/dist/react/option'; const App = () => ( <CwCombobox label="Skills" value={['javascript', 'css']} multiple clearable> <CwOption value="html">HTML</CwOption> <CwOption value="css">CSS</CwOption> <CwOption value="javascript">JavaScript</CwOption> <CwOption value="typescript">TypeScript</CwOption> <CwOption value="python">Python</CwOption> <CwOption value="go">Go</CwOption> </CwCombobox> );
Note that multi-select tags may wrap, causing the control to expand vertically. You can use the
max-options-visible attribute to control the maximum number of selected options to show at
once.
Filled & Pill
Add the filled attribute to draw a filled combobox, or pill to give it rounded
edges.
<cw-combobox placeholder="Filled" filled> <cw-option value="option-1">Option 1</cw-option> <cw-option value="option-2">Option 2</cw-option> <cw-option value="option-3">Option 3</cw-option> </cw-combobox> <br /> <cw-combobox placeholder="Pill" pill> <cw-option value="option-1">Option 1</cw-option> <cw-option value="option-2">Option 2</cw-option> <cw-option value="option-3">Option 3</cw-option> </cw-combobox>
import CwCombobox from '@cordwainer/cw-elements/dist/react/combobox'; import CwOption from '@cordwainer/cw-elements/dist/react/option'; const App = () => ( <> <CwCombobox placeholder="Filled" filled> <CwOption value="option-1">Option 1</CwOption> <CwOption value="option-2">Option 2</CwOption> <CwOption value="option-3">Option 3</CwOption> </CwCombobox> <br /> <CwCombobox placeholder="Pill" pill> <CwOption value="option-1">Option 1</CwOption> <CwOption value="option-2">Option 2</CwOption> <CwOption value="option-3">Option 3</CwOption> </CwCombobox> </> );
Sizes
Use the size attribute to change a combobox’s size. Note that size does not apply to listbox
options.
<cw-combobox placeholder="Small" size="small"> <cw-option value="option-1">Option 1</cw-option> <cw-option value="option-2">Option 2</cw-option> <cw-option value="option-3">Option 3</cw-option> </cw-combobox> <br /> <cw-combobox placeholder="Medium" size="medium"> <cw-option value="option-1">Option 1</cw-option> <cw-option value="option-2">Option 2</cw-option> <cw-option value="option-3">Option 3</cw-option> </cw-combobox> <br /> <cw-combobox placeholder="Large" size="large"> <cw-option value="option-1">Option 1</cw-option> <cw-option value="option-2">Option 2</cw-option> <cw-option value="option-3">Option 3</cw-option> </cw-combobox>
import CwCombobox from '@cordwainer/cw-elements/dist/react/combobox'; import CwOption from '@cordwainer/cw-elements/dist/react/option'; const App = () => ( <> <CwCombobox placeholder="Small" size="small"> <CwOption value="option-1">Option 1</CwOption> <CwOption value="option-2">Option 2</CwOption> <CwOption value="option-3">Option 3</CwOption> </CwCombobox> <br /> <CwCombobox placeholder="Medium" size="medium"> <CwOption value="option-1">Option 1</CwOption> <CwOption value="option-2">Option 2</CwOption> <CwOption value="option-3">Option 3</CwOption> </CwCombobox> <br /> <CwCombobox placeholder="Large" size="large"> <CwOption value="option-1">Option 1</CwOption> <CwOption value="option-2">Option 2</CwOption> <CwOption value="option-3">Option 3</CwOption> </CwCombobox> </> );
Prefix & Suffix
Use the prefix and suffix slots to add presentational icons and text. Avoid
slotting in interactive elements, such as buttons, links, etc.
<cw-combobox placeholder="Search" clearable> <cw-icon name="search" slot="prefix"></cw-icon> <cw-badge slot="suffix">New</cw-badge> <cw-option value="option-1">Option 1</cw-option> <cw-option value="option-2">Option 2</cw-option> <cw-option value="option-3">Option 3</cw-option> </cw-combobox>
import CwBadge from '@cordwainer/cw-elements/dist/react/badge'; import CwCombobox from '@cordwainer/cw-elements/dist/react/combobox'; import CwIcon from '@cordwainer/cw-elements/dist/react/icon'; import CwOption from '@cordwainer/cw-elements/dist/react/option'; const App = () => ( <CwCombobox placeholder="Search" clearable> <CwIcon name="search" slot="prefix"></CwIcon> <CwBadge slot="suffix">New</CwBadge> <CwOption value="option-1">Option 1</CwOption> <CwOption value="option-2">Option 2</CwOption> <CwOption value="option-3">Option 3</CwOption> </CwCombobox> );
No Matching Options
When the user’s input doesn’t match any option, a default “No matching options found.” message is shown in
the listbox. Use the no-options slot to customize this message.
<cw-combobox label="Search fruits"> <span slot="no-options">No fruits match your search.</span> <cw-option value="apple">Apple</cw-option> <cw-option value="banana">Banana</cw-option> <cw-option value="cherry">Cherry</cw-option> </cw-combobox>
import CwCombobox from '@cordwainer/cw-elements/dist/react/combobox'; import CwOption from '@cordwainer/cw-elements/dist/react/option'; const App = () => ( <CwCombobox label="Search fruits"> <span slot="no-options">No fruits match your search.</span> <CwOption value="apple">Apple</CwOption> <CwOption value="banana">Banana</CwOption> <CwOption value="cherry">Cherry</CwOption> </CwCombobox> );
Asynchronous Loading
Use the loading attribute to show a loading indicator in the listbox while options are being
fetched, for example in response to the cw-input event. Add or remove
<cw-option> elements as results come in — the combobox will automatically pick up newly
added options.
<cw-combobox label="Search a remote API" id="async-combobox"> <cw-option value="placeholder" disabled>Type to search…</cw-option> </cw-combobox> <script type="module"> const combobox = document.querySelector('#async-combobox'); let debounce; combobox.addEventListener('cw-input', () => { const query = combobox.displayInput.value.trim(); clearTimeout(debounce); if (!query) { return; } combobox.loading = true; debounce = setTimeout(() => { // Simulate a remote lookup const results = ['Alpha', 'Bravo', 'Charlie', 'Delta', 'Echo'] .filter(name => name.toLowerCase().includes(query.toLowerCase())); combobox.querySelectorAll('cw-option').forEach(option => option.remove()); results.forEach(name => { const option = document.createElement('cw-option'); option.value = name.toLowerCase(); option.textContent = name; combobox.append(option); }); combobox.loading = false; }, 500); }); </script>
Asynchronous loading is very hard to get right. <cw-combobox> largely follows the same
lazy-loading behavior as
<cw-select> — if an initial
value is set before matching options are connected, the combobox will adopt that value once a
matching <cw-option> is added.
Roadmap
The combobox currently supports filtering, multi-select with tags, custom values, and asynchronous loading. The following enhancements are planned for future releases:
-
Option grouping — group related options under headers (likely using
<cw-menu-label>), with roving focus skipping over the labels. -
“Create new” affordance — when
allow-custom-valueis set and the typed text matches nothing, show an explicit “Create ‘…’” option in the listbox instead of silently accepting it on blur/enter. - Highlight matching text — bold or otherwise highlight the matched substring within each option’s label while filtering.
-
Custom filter function — a
filterproperty so consumers can override the default substring match, e.g. for fuzzy matching or matching against hidden keywords/aliases. -
Virtualized listbox — render only visible options for very large option sets, pairing
naturally with
<cw-data-table>filter dropdowns. - Infinite scroll / “load more” — fetch additional pages of remote options as the user scrolls to the bottom of the listbox.
Importing
If you’re using the autoloader or the traditional loader, you can ignore this section. Otherwise, feel free to use any of the following snippets to cherry pick this component.
To import this component from the CDN using a script tag:
<script type="module" src="https://cdn.jsdelivr.net/npm/@cordwainer/cw-elements@1.1.1/cdn/components/combobox/combobox.js"></script>
To import this component from the CDN using a JavaScript import:
import 'https://cdn.jsdelivr.net/npm/@cordwainer/cw-elements@1.1.1/cdn/components/combobox/combobox.js';
To import this component using a bundler:
import '@cordwainer/cw-elements/dist/components/combobox/combobox.js';
To import this component as a React component:
import CwCombobox from '@cordwainer/cw-elements/dist/react/combobox';
Slots
| Name | Description |
|---|---|
| (default) |
The listbox options. Must be <cw-option> elements. You can use
<cw-divider> to group items visually.
|
label
|
The input’s label. Alternatively, you can use the label attribute. |
prefix
|
Used to prepend a presentational icon or similar element to the combobox. |
suffix
|
Used to append a presentational icon or similar element to the combobox. |
clear-icon
|
An icon to use in lieu of the default clear icon. |
expand-icon
|
The icon to show when the control is expanded and collapsed. Rotates on open and close. |
no-options
|
Content to show in the listbox when no options match the current filter. |
help-text
|
Text that describes how to use the input. Alternatively, you can use the
help-text attribute.
|
Learn more about using slots.
Properties
| Name | Description | Reflects | Type | Default |
|---|---|---|---|---|
name
|
The name of the combobox, submitted as a name/value pair with form data. |
string
|
''
|
|
value
|
The current value of the combobox, submitted as a name/value pair with form data. When
multiple is enabled, the value attribute will be a space-delimited list of values based
on the options selected, and the value property will be an array.
For this reason, values must not contain spaces.
|
- | - | |
defaultValue
value
|
The default value of the form control. Primarily used for resetting the form control. |
string | string[]
|
''
|
|
size
|
The combobox’s size. |
|
'small' | 'medium' | 'large'
|
'medium'
|
placeholder
|
Placeholder text to show as a hint when the combobox is empty. |
string
|
''
|
|
multiple
|
Allows more than one option to be selected. |
|
boolean
|
false
|
maxOptionsVisible
max-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.
|
number
|
3
|
|
disabled
|
Disables the combobox control. |
|
boolean
|
false
|
clearable
|
Adds a clear button when the combobox is not empty. |
boolean
|
false
|
|
allowCustomValue
allow-custom-value
|
When set, values typed by the user that don’t match any <cw-option> are accepted
as the control’s value. Only applies when multiple is not set.
|
boolean
|
false
|
|
loading
|
Shows a loading indicator and “no options” message in the listbox. Useful while options are being
fetched asynchronously in response to the cw-input event.
|
|
boolean
|
false
|
open
|
Indicates whether or not the listbox is open. You can toggle this attribute to show and hide the
listbox, or you can use the show() and hide() methods and this attribute
will reflect the combobox’s open state.
|
|
boolean
|
false
|
hoist
|
Enable this option to prevent the listbox from being clipped when the component is placed inside a
container with
overflow: auto|scroll. Hoisting uses a fixed positioning strategy that works in many,
but not all, scenarios.
|
boolean
|
false
|
|
filled
|
Draws a filled combobox. |
|
boolean
|
false
|
pill
|
Draws a pill-style combobox with rounded edges. |
|
boolean
|
false
|
label
|
The combobox’s label. If you need to display HTML, use the label slot instead. |
string
|
''
|
|
placement
|
The preferred placement of the combobox’s listbox. Note that the actual placement may vary as needed to keep the listbox inside of the viewport. |
|
'top' | 'bottom'
|
'bottom'
|
helpText
help-text
|
The combobox’s help text. If you need to display HTML, use the help-text slot instead.
|
string
|
''
|
|
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.
|
|
string
|
''
|
required
|
The combobox’s required attribute. |
|
boolean
|
false
|
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. |
(option: CwOption, index: number) => TemplateResult | string | HTMLElement
|
- | |
validity
|
Gets the validity state object | - | - | |
validationMessage
|
Gets the validation message | - | - | |
updateComplete |
A read-only promise that resolves when the component has finished updating. |
Learn more about attributes and properties.
Events
| Name | React Event | Description | Event Detail |
|---|---|---|---|
cw-change |
onCwChange |
Emitted when the control’s value changes. | - |
cw-clear |
onCwClear |
Emitted when the control’s value is cleared. | - |
cw-input |
onCwInput |
Emitted when the control receives input, including as the user types to filter options. | - |
cw-focus |
onCwFocus |
Emitted when the control gains focus. | - |
cw-blur |
onCwBlur |
Emitted when the control loses focus. | - |
cw-show |
onCwShow |
Emitted when the combobox’s listbox opens. | - |
cw-after-show |
onCwAfterShow |
Emitted after the combobox’s listbox opens and all animations are complete. | - |
cw-hide |
onCwHide |
Emitted when the combobox’s listbox closes. | - |
cw-after-hide |
onCwAfterHide |
Emitted after the combobox’s listbox closes and all animations are complete. | - |
cw-invalid |
onCwInvalid |
Emitted when the form control has been checked for validity and its constraints aren’t satisfied. | - |
Learn more about events.
Methods
| Name | Description | Arguments |
|---|---|---|
show() |
Shows the listbox. | - |
hide() |
Hides the listbox. | - |
checkValidity() |
Checks for validity but does not show a validation message. Returns true when valid and
false when invalid.
|
- |
getForm() |
Gets the associated form, if one exists. | - |
reportValidity() |
Checks for validity and shows the browser’s validation message if the control is invalid. | - |
setCustomValidity() |
Sets a custom validation message. Pass an empty string to restore validity. |
message: string
|
focus() |
Sets focus on the control. |
options: FocusOptions
|
blur() |
Removes focus from the control. | - |
Learn more about methods.
Parts
| Name | Description |
|---|---|
form-control |
The form control that wraps the label, input, and help text. |
form-control-label |
The label’s wrapper. |
form-control-input |
The combobox’s wrapper. |
form-control-help-text |
The help text’s wrapper. |
combobox |
The container that wraps the prefix, suffix, input, tags, clear icon, and expand button. |
prefix |
The container that wraps the prefix slot. |
suffix |
The container that wraps the suffix slot. |
display-input |
The component’s <input> element. |
listbox |
The listbox container where options are slotted. |
no-options |
The container shown when no options match the current filter. |
loading |
The container shown while loading is set. |
tags |
The container that houses option tags when multiple is used. |
tag |
The individual tags that represent each selected option. |
tag__base |
The tag’s base part. |
tag__content |
The tag’s content part. |
tag__remove-button |
The tag’s remove button. |
tag__remove-button__base |
The tag’s remove button base part. |
clear-button |
The clear button. |
expand-icon |
The container that wraps the expand icon. |
Learn more about customizing CSS parts.
Dependencies
This component automatically imports the following dependencies.
<cw-icon><cw-icon-button><cw-popup><cw-spinner><cw-tag><cw-visually-hidden>