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
└── ComboboxItemEach 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.
<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.
<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.
<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
| Prop | Type | Default | Description |
|---|---|---|---|
modelValue | string | none | The 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. |
offset | number | 4 | Gap (px) between input and panel. |
class | string | none | Classes merged onto the root. |
ComboboxInput
| Prop | Type | Default | Description |
|---|---|---|---|
placeholder | string | "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. |
class | string | none | Classes merged onto the input wrapper. |
ComboboxContent
| Prop | Type | Default | Description |
|---|---|---|---|
width | "fit" | "full" | "3xs" | "2xs" | "xs" | "sm" | "md" | "lg" | "full" | Panel width. full matches the input; a token gives a fixed width. |
class | string | none | Classes merged onto the panel. |
ComboboxItem
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | none | The option's value (what v-model receives). Required. |
label | string | none | Display text; also what the input filters against. Falls back to slot text if omitted. |
disabled | boolean | false | Makes 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,Enterto select,Escapeto close, androlewiring 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 aLabel) so the control has an accessible name.