--- layout: layouts/page.njk title: Combobox description: Autocomplete input and command palette with a list of suggestions. toc: - label: Usage id: usage children: - label: HTML + JavaScript id: usage-html-js children: - label: "Step 1: Include the JavaScript files" id: usage-html-js-1 - label: "Step 2: Add your combobox HTML" id: usage-html-js-2 - label: HTML structure id: usage-html-js-3 - label: JavaScript events id: usage-html-js-4 - label: JavaScript methods id: usage-html-js-5 - label: Jinja and Nunjucks id: usage-macro - label: Examples id: examples children: - label: Scrollable id: example-scrollable - label: Top side id: example-top --- {% from "macros/code_preview.njk" import code_preview %} {% from "macros/code_block.njk" import code_block %} {% from "select.njk" import select %} {% set options_frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"] %} {% set code_html %} {% call select( trigger_attrs={"class": "w-[200px]"}, listbox_attrs={"data-empty": "No framework found."}, is_combobox=true, search_placeholder="Search framework..." ) %} {% for framework in options_frameworks %}
{{ framework }}
{% endfor %} {% endcall %} {% endset %} {{ code_preview("combobox", code_html | prettyHtml) }}

Usage

HTML + JavaScript

Step 1: Include the JavaScript files

You can either include the JavaScript file for all the components, or just the one for this component by adding this to the <head> of your page:

{% set code_script %} {% endset %} {{ code_block(code_script | prettyHtml, "html") }}
Components with JavaScript {% lucide "arrow-right" %} Use the CLI {% lucide "arrow-right" %} select.js {% lucide "arrow-right" %}

Step 2: Add your combobox HTML

Combobox uses the same markup and JavaScript as the Select component, the only difference being the search box at the top of the listbox:

{{ code_block(code_html | prettyHtml, "html") }}

HTML structure

<div class="select">
Wraps around the entire component.
<button type="button" popovertarget="{ POPOVER_ID }">

The trigger to open the popover, can also have the following attributes:

  • id="{BUTTON_ID}": linked to by the aria-labelledby attribute of the listbox.
  • aria-haspopup="listbox": indicates that the button opens a listbox.
  • aria-controls="{ LISTBOX_ID }": points to the listbox's id.
  • aria-expanded="false": tracks the popover's state.
  • aria-activedescendant="{ OPTION_ID }": points to the active option's id.
<div popover class="popover" id="{ POPOVER_ID }">
As with the Popover component, you can set up the side and alignment of the popover using the data-side and data-align attributes.
<header>
Contains the search input to filter the options in the listbox.
<div role="listbox">
The listbox containing the options. Should have the following attributes:
  • id="{ LISTBOX_ID }": refered to by the aria-controls attribute of the trigger.
  • aria-labelledby="{ BUTTON_ID }": linked to by the button's id attribute.
<div role="option" data-value="{ VALUE }">
Option that can be selected.Should have a unique id if you use the aria-activedescendant attribute on the trigger.
<hr role="separator"> Optional
Separator between groups/options.
<div role="group"> Optional
Group of options, can have a aria-labelledby attribute to link to a heading.
<span role="heading">
Group heading, must have an id attribute if you use the aria-labelledby attribute on the group.
<input type="hidden" name="{ NAME }" value="{ VALUE }"> Optional
The hidden input that holds the value of the field (if needed).

JavaScript events

basecoat:initialized
Once the component is fully initialized, it dispatches a custom (non-bubbling) basecoat:initialized event on itself.
basecoat:popover
When the popover opens, the component dispatches a custom (non-bubbling) basecoat:popover event on document. Other popover components (Combobox, Dropdown Menu, Popover and Select) listen for this to close any open popovers.
change
When the selected value changes, the component dispatches a custom (bubbling) change event on itself, with the selected value in event.detail (e.g. {detail: {value: "something"}}).

JavaScript methods

selectByValue

You can call this method on the component after it is initialized to select an option by value (i.e. the option with the matching data-value attribute):

{% set code_script %}{% endset %} {{ code_block(code_script | prettyHtml, "html") }}

Jinja and Nunjucks

You can use the select() Nunjucks or Jinja macro for this component. If you use one of the macros, make sure to set is_combobox to True (or true for Nunjucks).

Use Nunjucks or Jinja macros {% lucide "arrow-right" %} Jinja macro {% lucide "arrow-right" %} Nunjucks macro {% lucide "arrow-right" %}
{% set raw_code %}{% raw %}{% call select( listbox_attrs={"data-empty": "No framework found."}, is_combobox=true ) %}
Next.js
SvelteKit
Nuxt.js
Remix
Astro
{% endcall %}{% endraw %}{% endset %} {{ code_block(raw_code, "jinja") }}

Examples

Scrollable

{% set options_timezones = [ { label: "Americas", timezones: [ { value: "America/New_York", label: "(GMT-5) New York" }, { value: "America/Los_Angeles", label: "(GMT-8) Los Angeles" }, { value: "America/Chicago", label: "(GMT-6) Chicago" }, { value: "America/Toronto", label: "(GMT-5) Toronto" }, { value: "America/Vancouver", label: "(GMT-8) Vancouver" }, { value: "America/Sao_Paulo", label: "(GMT-3) São Paulo" } ] }, { label: "Europe", timezones: [ { value: "Europe/London", label: "(GMT+0) London" }, { value: "Europe/Paris", label: "(GMT+1) Paris" }, { value: "Europe/Berlin", label: "(GMT+1) Berlin" }, { value: "Europe/Rome", label: "(GMT+1) Rome" }, { value: "Europe/Madrid", label: "(GMT+1) Madrid" }, { value: "Europe/Amsterdam", label: "(GMT+1) Amsterdam" } ] }, { label: "Asia/Pacific", timezones: [ { value: "Asia/Tokyo", label: "(GMT+9) Tokyo" }, { value: "Asia/Shanghai", label: "(GMT+8) Shanghai" }, { value: "Asia/Singapore", label: "(GMT+8) Singapore" }, { value: "Asia/Dubai", label: "(GMT+4) Dubai" }, { value: "Australia/Sydney", label: "(GMT+11) Sydney" }, { value: "Asia/Seoul", label: "(GMT+9) Seoul" } ] } ] %} {% set code %} {% call select( trigger_attrs={"class": "w-[200px]"}, popover_attrs={"class": "w-72"}, listbox_attrs={"data-empty": "No timezone found."}, is_combobox=true, search_placeholder="Search timezone..." ) %}
{% for group in options_timezones %}
{{ group.label }}
{% for timezone in group.timezones %}
{{ timezone.label }}
{% endfor %}
{% endfor %}

{% lucide "circle-plus" %} Create timezone
{% endcall %} {% endset %} {{ code_preview("combobox-with-groups", code | prettyHtml) }}

Top side

{% set options_frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"] %} {% set code %} {% call select( trigger_attrs={"class": "w-[200px]"}, listbox_attrs={"data-empty": "No framework found."}, is_combobox=true, search_placeholder="Search framework...", popover_attrs={"data-side": "top"} ) %} {% for framework in options_frameworks %}
{{ framework }}
{% endfor %} {% endcall %}{% endset %} {{ code_preview("combobox", code | prettyHtml) }}