StartFoundationsComponentsContent

Combobox

A combobox lets people choose one option from a long list by typing to filter it. The value is always one of your options; typed text only finds it.

Stable Figma
OverviewUsageAPIAccessibility
<tk-field label="Country">
  <tk-combobox name="country" placeholder="Start typing a country">
    <option value="dk">Denmark</option>
    <option value="de">Germany</option>
    <option value="nl">Netherlands</option>
    <option value="gb">United Kingdom</option>
  </tk-combobox>
</tk-field>

Put the combobox in a Field to give it a label, helper, and error. Without a Field, set aria-label. A placeholder is not a name: it disappears as soon as people type.

Anatomy

Part What it is
tk-combobox the element; one Tab stop
field a text input: shows the chosen option, or what people type; fills its container
clear button appears while the field has text; clears the text and the value
chevron turns over while the menu is open; a spinner replaces it while options load
menu the matching options, as wide as the field; opens below, or above near the bottom of the screen
option one row; a check marks the chosen one
group header an optional heading over a run of options; hidden when none of its options match
message row "No results found", a loading hint, or a load error; it can't be chosen

The menu shows about five and a half rows, then scrolls.

Sizes

<tk-combobox aria-label="City" placeholder="Start typing a city">
  <option>Amsterdam</option>
</tk-combobox>
<tk-combobox size="large" aria-label="Airport" placeholder="Start typing an airport">
  <option>Schiphol</option>
</tk-combobox>
size Height Padding-inline Text Option row
medium (default) 32px space-8 Body Small (14/20) 32px
large 36px space-10 Body Medium (16/24) 36px

The heights match Select, Input, and Button. Long options are cut off with an ellipsis.

States

<tk-combobox aria-label="Office" value="lis" disabled>
  <option value="lis">Lisbon</option>
</tk-combobox>
<tk-combobox aria-label="Team" value="core" readonly>
  <option value="core">Core platform</option>
</tk-combobox>

Hover doesn't change the field. Keyboard focus colors its border and thickens it; there is no ring.

  • disabled: can't be focused or opened, and isn't submitted.
  • readonly: shows the value and submits it. People can focus and copy it, but can't edit it or open the menu. The clear button and chevron are hidden.
  • Error: set error on the Field. The message appears below and the border turns red, even while focused.

Filtering

  • Typing opens the menu and filters it. Matching ignores case and finds text anywhere in the label: "kingdom" finds "United Kingdom". Descriptions and group headers aren't searched.
  • The text is a search, not the value. The value changes only when someone chooses an option. If they leave without choosing, the text goes back to the chosen option.
  • A click opens the full list, so people can choose without typing.
  • With no match, the menu shows "No results found".

Data sources

Source How Filtering
<option> children (default) markup, with <optgroup> for groups in the browser
options property an array of { value, label, description?, disabled?, group? } in the browser
loadOptions property an async function that returns options for a query on your server

Use loadOptions only when the list is remote or too large to ship:

combobox.loadOptions = async (query, { signal }) => {
  const res = await fetch(`/api/cities?q=${encodeURIComponent(query)}`, { signal });
  return (await res.json()).map((c) => ({ value: c.id, label: c.name }));
};
  • Requests wait debounce ms (250) after the last keystroke.
  • min-chars holds requests until enough is typed; the menu says "Type 2 or more characters." The default, 0, loads the full list on click.
  • While loading, a spinner replaces the chevron. If the promise rejects, the menu shows "Couldn't load options." with Try again; Enter also retries.
  • Only the latest query's results show. Pass signal to fetch to cancel older requests.
  • Return the matches already filtered, one line each.

Option descriptions

<tk-combobox aria-label="Plan" placeholder="Start typing a plan">
  <option value="free" data-description="For trying things out">Free</option>
  <option value="pro" data-description="Adds version history">Pro</option>
</tk-combobox>

data-description (or description in options) adds a short second line. Use it to help people choose; don't describe every option. Screen readers read it after the label.

Creatable

<tk-field label="Tag">
  <tk-combobox creatable placeholder="Start typing a tag">
    <option value="design">Design</option>
    <option value="research">Research</option>
  </tk-combobox>
</tk-field>

By default people can only choose your options. With creatable, a query that matches no option adds a Create "…" row. Choosing it, or pressing Enter on the typed text, sets that text as the value and adds it to the list. Use it for open sets, such as tags or skills.

  • An exact match, ignoring case, offers the option instead of creating a copy.
  • Leaving the field with Tab, Esc, or a click outside creates nothing.
  • It works with every data source, but not while loading or below min-chars.