Skip to content

Combobox ​

A text input paired with a filterable list, the user types to narrow the options, then picks one. Use it instead of a Select when the list is long enough that filtering helps. Built on Headless UI's combobox, so keyboard navigation and accessibility come for free.

Combobox is a compound component, and it mirrors Select's structure, with a filterable input in place of the button trigger.

Anatomy ​

Combobox
├── ComboboxInput
├── ComboboxContent
└── ComboboxItem

Each option is a ComboboxItem with a value and a label. The input filters the items by their label as you type, and shows the selected label once you pick.

Usage ​

Type to filter; pick an option to select it.

BasicType to filter a list of options.
<script setup lang="ts">
import { ref } from "vue";
import {
    Combobox,
    ComboboxInput,
    ComboboxContent,
    ComboboxItem,
    ComboboxEmpty,
} from "@dlbcodes/ui";

const value = ref("");
</script>

<template>
    <div class="w-full max-w-xs">
        <Combobox v-model="value">
            <ComboboxInput placeholder="Search frameworks..." />
            <ComboboxContent>
                <ComboboxItem value="vue" label="Vue">Vue</ComboboxItem>
                <ComboboxItem value="react" label="React">React</ComboboxItem>
                <ComboboxItem value="svelte" label="Svelte"
                    >Svelte</ComboboxItem
                >
                <ComboboxItem value="angular" label="Angular"
                    >Angular</ComboboxItem
                >
                <ComboboxItem value="solid" label="Solid">Solid</ComboboxItem>
                <ComboboxItem value="qwik" label="Qwik">Qwik</ComboboxItem>
                <ComboboxEmpty>No frameworks match.</ComboboxEmpty>
            </ComboboxContent>
        </Combobox>
    </div>
</template>

Pre-filled value ​

Because each item carries its label, a combobox bound to a value displays the correct label immediately, before any interaction.

Pre-filledBound to a value on load, shows its label right away.
<script setup lang="ts">
import { ref } from "vue";
import {
    Combobox,
    ComboboxInput,
    ComboboxContent,
    ComboboxItem,
    ComboboxEmpty,
} from "@dlbcodes/ui";

const value = ref("react");
</script>

<template>
    <div class="w-full max-w-xs">
        <Combobox v-model="value">
            <ComboboxInput placeholder="Search frameworks..." />
            <ComboboxContent>
                <ComboboxItem value="vue" label="Vue">Vue</ComboboxItem>
                <ComboboxItem value="react" label="React">React</ComboboxItem>
                <ComboboxItem value="svelte" label="Svelte"
                    >Svelte</ComboboxItem
                >
                <ComboboxItem value="angular" label="Angular"
                    >Angular</ComboboxItem
                >
                <ComboboxEmpty>No frameworks match.</ComboboxEmpty>
            </ComboboxContent>
        </Combobox>
    </div>
</template>

Inside a Field ​

Wrap it in a Field for a label, description, and accessibility wiring, just like the other form controls.

With FieldA labelled combobox with a description.
<script setup lang="ts">
import { ref } from "vue";
import {
    Combobox,
    ComboboxInput,
    ComboboxContent,
    ComboboxItem,
    ComboboxEmpty,
    Field,
    FieldLabel,
    FieldContent,
    FieldDescription,
} from "@dlbcodes/ui";

const country = ref("");
</script>

<template>
    <div class="w-full max-w-xs">
        <Field>
            <FieldLabel>Country</FieldLabel>
            <FieldContent>
                <Combobox v-model="country">
                    <ComboboxInput placeholder="Search countries..." />
                    <ComboboxContent>
                        <ComboboxItem value="pt" label="Portugal"
                            >Portugal</ComboboxItem
                        >
                        <ComboboxItem value="es" label="Spain"
                            >Spain</ComboboxItem
                        >
                        <ComboboxItem value="fr" label="France"
                            >France</ComboboxItem
                        >
                        <ComboboxItem value="de" label="Germany"
                            >Germany</ComboboxItem
                        >
                        <ComboboxItem value="it" label="Italy"
                            >Italy</ComboboxItem
                        >
                        <ComboboxEmpty>No frameworks match.</ComboboxEmpty>
                    </ComboboxContent>
                </Combobox>
            </FieldContent>
            <FieldDescription
                >Start typing to filter the list.</FieldDescription
            >
        </Field>
    </div>
</template>

Combobox vs Select ​

Both let the user pick one value from a set. Reach for Combobox when the list is long and filtering by typing helps; reach for Select when the list is short enough to scan and a filter would be overkill.

Props ​

Combobox ​

PropTypeDefaultDescription
modelValuestringnoneThe selected value. Use with v-model. Required.
placement"bottom-start" | "bottom-end" | "bottom" | "top-start" | "top-end" | "top""bottom-start"Where the panel opens relative to the input.
offsetnumber4Gap (px) between input and panel.
classstringnoneClasses merged onto the root.

ComboboxInput ​

PropTypeDefaultDescription
placeholderstring"Search..."Shown when nothing is typed/selected.
variant"primary" | "contrast""primary"Field style (shares Input's variants).
size"sm" | "base" | "lg" | "xl""base"Field size (shares Input's sizes). Heights are 32, 40, 48 and 56px, the same as Button, so they line up in a row.
classstringnoneClasses merged onto the input wrapper.

ComboboxContent ​

PropTypeDefaultDescription
width"fit" | "full" | "3xs" | "2xs" | "xs" | "sm" | "md" | "lg""full"Panel width. full matches the input; a token gives a fixed width.
classstringnoneClasses merged onto the panel.

ComboboxItem ​

PropTypeDefaultDescription
valuestringnoneThe option's value (what v-model receives). Required.
labelstringnoneDisplay text; also what the input filters against. Falls back to slot text if omitted.
disabledbooleanfalseMakes the option non-selectable and skipped by keyboard nav.

Provide label so the input displays and filters correctly, including for a pre-filled value. The slot controls how the option appears in the list.

Accessibility ​

  • Built on Headless UI's Combobox, so type-ahead filtering, arrow-key navigation, Enter to select, Escape to close, and role wiring are handled.
  • The selected option shows a check and is marked selected for assistive tech.
  • Disabled options are skipped by keyboard navigation.
  • Wrap in a Field (or pair with a Label) so the control has an accessible name.