Edit Profile

Dark Mode

Copy config

Copy and paste the following code into your global.css file to apply the styles.

DISCLAIMER: This component is in
Beta
status. That means that it is ready for production, but the API might change.

Combobox

Users can either type a value or pick one from a dropdown list.

✨ Features

  • WAI ARIA Combobox design pattern
  • Single and multiple selection
  • Reactive and initial value changes
  • Disabled items
  • Tab key focus management
  • Grouped items
  • Looping
  • Custom scroll behavior
  • Popover UI above all
  • Custom positioning (Popover)
  • Typeahead item selection and focus
  • Arrow key navigation and focus management
  • Open/Close popover by typing, focus, or manually
  • Custom filter function
  • Closes on no matching items
  • Roadmap

  • Browser autofill with hidden select
  • Form validation support
  • Custom placeholder

Building blocks

import { component$ } from '@builder.io/qwik';
import { Combobox } from '@qwik-ui/headless';
import { LuCheck } from '@qwikest/icons/lucide';

export default component$(() => {
  return (
    <Combobox.Root>
      <Combobox.Label>label</Combobox.Label>

      <Combobox.Control>
        <Combobox.Input />
        <Combobox.Trigger>trigger</Combobox.Trigger>
      </Combobox.Control>

      <Combobox.Popover>
        <Combobox.Item>
          <Combobox.ItemLabel>item label</Combobox.ItemLabel>
          <Combobox.ItemIndicator>
            <LuCheck />
          </Combobox.ItemIndicator>
        </Combobox.Item>
      </Combobox.Popover>
    </Combobox.Root>
  );
});

🎨 Anatomy

ComponentDescription
Combobox.Root

Container for the Combobox.

Combobox.Label

Accessible label for the Combobox.

Combobox.Control

Central element around which other elements are positioned.

Combobox.Trigger

Opens and closes the Combobox.

Combobox.Input

Filters items and shows the selected value.

Combobox.Popover

Container for list items, positioned above other content. Also the popover.

Combobox.Item

item in the popover.

Combobox.ItemLabel

Label for an item.

Combobox.ItemIndicator

Indicates selection.

Combobox.Group

Groups related items.

Combobox.GroupLabel

Label for a group of items.

Combobox.Description

Displays the accessible description of the combobox.

Why use a headless Combobox?

A native combobox does not exist. The Open UI group has created a proposal for a new HTML element called combobox.

Passing data

To add data, use the <Combobox.Item> component inside of the popover.

Basic example

By default, the content inside of the <Combobox.ItemLabel /> component is the item's value.

Mapping over data

You control how the data is rendered. Map over the data or render the items as you like.

Object example

Passing a distinct value

The selected value is: null

A distinct value is when one thing is displayed to the user, but another value is passed to the item.

By adding the value prop to the <Combobox.Item /> component, a distinct value is created.

Handling selection changes

You have changed 0 times

Use the onChange$ prop to listen for changes in the selected value. It provides the new selected value as an argument.

The example above increments a count when a new item is selected.

Component state

To add initial state, use the value prop on the <Combobox.Root /> component.

Uncontrolled / Initial value

The example above sets "Jessie" as the initial value, so it is selected and focused.

Controlled / Reactive value

To add reactive state, use the bind:value prop on the <Combobox.Root /> component.

Your favorite fruit is: Apricot

Programmatic changes

Optionally, the selected value can be programmatically updated by the signal.

In the example above, clicking the "Change to Abby" button changes the selected value.

Disabled items

Add the disabled prop to <Combobox.Item /> to disable items.

Disabled items cannot be selected or focused and are skipped during arrow key navigation.

Dynamically adding items

A common use case is adding items dynamically, like an infinite scrolling list of users.

Clicking the Add Users button creates new users to the list. We could also fetch more data from the server or database.

Item indicators

Add <Combobox.ItemIndicator /> inside <Combobox.Item /> to indicate the selected item.

Multiple selections

To allow multiple selections, set the multiple prop to true.

Use the bind:displayValue signal to configure multiple display values.

You can combine bind:value and bind:displayValue to create a custom widget that displays selected values as a list of pills.

Remove items on backspace

To remove items on backspace, add the removeOnBackspace prop to the <Combobox.Root /> component.

Placeholder

We can provide a custom placeholder to the <Combobox.Root /> component by adding the placeholder prop.

CSR

Like every Qwik UI component, the Combobox component can be rendered server-side or client-side.

Custom Filter

By default, the Combobox filters items based on user input. To disable this, set the filter prop to false.

You can use any filtering library. In the example above, we use match-sorter.

Empty UI

By default, the popover automatically closes when there are no items to display.

To show UI when there are no items in the popover, use the Combobox.Empty component.

Handling popover open/close

To handle the popover open/close state, use the onOpenChange$ prop. It passes a boolean indicating whether the popover is open.

The listbox opened and closed 0 time(s)

Reactive open state

To reactively control the open state, use the bind:open prop.

Handling input changes

Add the onInput$ prop to the <Combobox.Root /> component.

onInput$ value:

onInput$ was called 0 time(s)

This function is called when the user types in the input.

Reactive input value

The input can also be fully controlled by adding the bind:value prop to the <Combobox.Input /> component.

Typed input string: aba

Looping

To loop through items, use the loop prop on <Combobox.Root />.

  • Pressing the down arrow key moves focus to the first enabled item.
  • Pressing the up arrow key moves focus to the last enabled item.

Grouped Items

Use <Combobox.Group /> and <Combobox.GroupLabel /> to group items and provide an accessible name.

Scrolling

Handle scrolling in the popover since focus remains on the select trigger when open.

The native scrollIntoView method scrolls items into view when highlighted. To customize scroll behavior, add the scrollOptions prop to <Combobox.Root />.

When a value is not selected, the placeholder is displayed.

Advanced

Using Refs (experimental)

You can pass a ref to each Combobox component to access the underlying DOM element. This is an experimental feature, and we are still working on it, use at your own risk.

Click the trigger to change the background color

Above is an example that programmatically changes the background color of the trigger when clicked.

Forms

To use the combobox in a form, a visually hidden native select element is provided inside of <Combobox.HiddenNativeSelect>.

The name prop on the Combobox.Root component is used to name the Combobox form field.

Validation

Form libaries like Modular Forms can be used to validate the combobox form field.

When the <Combobox.ErrorMessage> component is rendered, the component is in an invalid state.

Example CSS

Every code example uses the following CSS:

:root {
  --combobox-width: 12rem;
}

.combobox-root {
  min-width: var(--combobox-width);
}

.combobox-input {
  all: unset;
  box-sizing: border-box;
  background: transparent;
  padding-inline: 0.5rem;
  padding-right: 44px;
}

.combobox-trigger {
  border-radius: calc(var(--border-radius) / 2);
  width: 44px;
  background: hsla(var(--primary) / 0.1);
  display: flex;
  align-items: center;
  justify-content: center;
  border-left: 2px dotted hsla(var(--foreground));
  position: absolute;
  right: 0;
}

.combobox-trigger[data-open] svg {
  transform: rotate(180deg);
}

.combobox-trigger,
.combobox-input {
  height: 44px;
}

.combobox-control {
  min-height: 48px;
  display: flex;
  align-items: center;
  outline: 2px dotted hsla(var(--foreground));
  position: relative;
}

.combobox-control:focus-within {
  background: hsl(var(--primary) / 0.08);
}

.combobox-control[data-invalid] {
  outline: 2px dotted #d2122e;
}

.combobox-popover {
  width: 100%;
  max-width: calc(var(--combobox-width) + 2.5rem);
  background-color: hsl(var(--background));
  padding: 0.5rem;
  border: 2px dotted hsla(var(--foreground) / 0.6);
  border-radius: calc(var(--border-radius) / 2);
  color: hsl(var(--foreground));
}

.combobox-icon {
  width: 1.5rem;
  height: 1.5rem;
  stroke: hsl(var(--foreground));
}

.combobox-trigger[data-open] .combobox-icon {
  transform: rotate(180deg);
}

.combobox-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.combobox-label {
  font-size: 0.875rem;
  line-height: 1.25rem;
  color: hsla(var(--foreground) / 0.8);
  padding-top: 0.5rem;
}

.combobox-max-height {
  max-height: 12rem;
  overflow-y: auto;
}

.combobox-group-label {
  font-size: 0.875rem;
  line-height: 1.25rem;
  color: hsla(var(--foreground) / 0.8);
  padding-top: 0.5rem;
}

[data-highlighted] {
  outline: 2px dotted hsla(var(--primary) / 1);
  background: hsla(var(--primary) / 0.08);
  border-radius: calc(var(--border-radius) / 2);
}

[data-disabled] {
  opacity: 0.6;
  background: hsl(var(--foreground) / 0.05);
}

.combobox-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  margin-inline: 0.25rem;
  padding: 0.25rem 0.5rem;
  background-color: hsl(var(--muted));
  margin-block: 0.25rem;
}

.combobox-pill-container {
  padding-right: 44px;
  width: 100%;
}

.combobox-multiple {
  display: flex;
  flex-direction: column;
}

.combobox-multiple input {
  width: 100%;
}

.combobox-multiple .combobox-trigger {
  min-height: 48px;
  height: 100%;
  aspect-ratio: 1 / 1;
}

.combobox-clear {
  background: hsl(var(--secondary) / 0.08);
}

.combobox-control:focus-within .combobox-pill {
  background-color: hsl(var(--foreground) / 0.08);
}

.combobox-control:focus-within .combobox-clear {
  background: hsl(var(--secondary) / 0.25);
}

Some CSS variables are specific to the docs, feel free to plug in your own values or variables!

Keyboard Interaction

Key

Description

Enter
Selects a highlighted item when open. Toggles the selection of the highlighted item without closing the combobox if an item is already selected.
ArrowDown
Opens the combobox or moves focus down.
ArrowUp
Opens the combobox or moves focus up.
Home
When focus is on an item, moves focus to first item.
End
When focus is on an item, moves focus to last item.
Esc
Closes the combobox and moves focus to the trigger.
Tab
Moves focus to the next focusable element.

Multi Select

When in multi select mode, additional keyboard interactions are available.

Key

Description

Enter
Toggles the selection of the highlighted item without closing the combobox.

API

Data Attributes

Combobox.Root, Combobox.Trigger, and Combobox.Popover, all have data attributes that are used to track state.

AttributeDescription
data-open

If the popover is open (Boolean).

data-closed

If the popover is closed (Boolean).

data-invalid

If the combobox is invalid (Boolean).

Combobox.Item has the following data attributes:

AttributeDescription
data-selected

If the item is selected. (Boolean)

data-highlighted

If the item is highlighted. (Boolean)

data-disabled

If item is disabled. (Boolean)

Combobox.Root

PropTypeDescription
value
string

Uncontrolled combobox value.

bind:value
signal

Controlled selected value, manages the selected item.

filter
boolean

Disables the default filter function when set to false.

onChange$
QRL

Function called when the selected value changes.

bind:open
signal

Controlled open state of the popover.

onOpenChange$
QRL

Function called when the popover opens or closes.

loop
boolean

Determines if focus cycles from the last item back to the first, or vice versa.

placeholder
string

Sets a placeholder instead of a selected value.

multiple
boolean

Enables multiple selections.

bind:displayValue
signal

Controlled display value, manages the display items.

onInput$
QRL

Function called when the user types in the input.

Combobox.Popover

Combobox.Popover is a wrapper of the Popover component, and has the same APIs.

Combobox.Item:

PropTypeDescription
value
string

Give the combobox a value other than what is displayed in the item.

disabled
boolean

When true, the item is disabled.