Skip to main content
Light Dark System

Combobox

<cw-combobox> | CwCombobox
Since 1.1 experimental

Comboboxes combine a text input with a listbox, allowing users to filter a list of options by typing.

Apple Apricot Banana Blueberry Cherry Grape Mango Orange Peach Pear
<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>
);

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.

Alex Johnson Bailey Smith Casey Lee Drew Patel
<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.

Option 1 Option 2 Option 3
<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.

Option 1 Option 2 Option 3
<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.

Option 1 Option 2 Option 3
<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.

Bug Enhancement Documentation
<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.

HTML CSS JavaScript TypeScript Python Go
<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>
);

Filled & Pill

Add the filled attribute to draw a filled combobox, or pill to give it rounded edges.

Option 1 Option 2 Option 3
Option 1 Option 2 Option 3
<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.

Option 1 Option 2 Option 3
Option 1 Option 2 Option 3
Option 1 Option 2 Option 3
<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.

New Option 1 Option 2 Option 3
<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.

No fruits match your search. Apple Banana Cherry
<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.

Type to search…
<cw-combobox label="Search a remote API" id="async-combobox">
  <cw-option value="placeholder" disabled>Type to search&hellip;</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>

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-value is 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 filter property 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.

Script Import Bundler React

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>