Combobox
Filtering and empty state
Headless enables filter by default — non-matching items hide as you type. With Combobox.Empty you show a message when nothing remains.
import { Combobox } from "~/components/ui/base/combobox";
<Combobox.Root filter placeholder="Search framework…">
<Combobox.Label>Framework</Combobox.Label>
<Combobox.Control>
<Combobox.Input />
<Combobox.Trigger>▼</Combobox.Trigger>
</Combobox.Control>
<Combobox.Popover>
<Combobox.Item value="qwik">
<Combobox.ItemLabel>Qwik</Combobox.ItemLabel>
</Combobox.Item>
<Combobox.Item value="react">
<Combobox.ItemLabel>React</Combobox.ItemLabel>
</Combobox.Item>
<Combobox.Empty>No match.</Combobox.Empty>
</Combobox.Popover>
</Combobox.Root>
Controlled value (bind:value)
The selected value in a signal via bind:value (controlled combobox).
Value: praha
import { component$, useSignal } from "@builder.io/qwik";
import { Combobox } from "~/components/ui/base/combobox";
export const Controlled = component$(() => {
const value = useSignal("praha");
return (
<>
<Combobox.Root bind:value={value} filter placeholder="City…">
<Combobox.Label>City</Combobox.Label>
<Combobox.Control>
<Combobox.Input />
<Combobox.Trigger>▼</Combobox.Trigger>
</Combobox.Control>
<Combobox.Popover>
<Combobox.Item value="praha">
<Combobox.ItemLabel>Prague</Combobox.ItemLabel>
</Combobox.Item>
<Combobox.Item value="brno">
<Combobox.ItemLabel>Brno</Combobox.ItemLabel>
</Combobox.Item>
<Combobox.Empty>Nothing found.</Combobox.Empty>
</Combobox.Popover>
</Combobox.Root>
<p class="mt-2 text-caption-1 text-secondary-label">Value: {value.value}</p>
</>
);
});
Multiple selection and chips
With multiple and bind:value as string[] (without a one-off value on the root — headless does not allow it in multi mode). Render the selected items as Combobox.Chip with onRemove ; filtering them out of the array synchronizes the state. On Combobox.Control use comboboxMultiselectControlClass and on the input comboboxMultiselectInputClass . Without Combobox.Trigger (no arrow): open the list on input focus via bind:open and onFocus . Keep the input text in a separate signal via Combobox.Input bind:value (default empty string) — otherwise headless would fill the input with the names of the selected items when multiple; the selection stays only on the chips. After a selection change, clear the filter in onChange on the root. In the list, mark the selected rows via Combobox.ItemIndicator` (headless shows them only for selected items).
import { $, component$, useSignal } from "@builder.io/qwik";
import {
Combobox,
comboboxMultiselectControlClass,
comboboxMultiselectInputClass,
} from "~/components/ui/base/combobox";
const LANGS = [
{ value: "ts", label: "TypeScript" },
{ value: "rust", label: "Rust" },
{ value: "go", label: "Go" },
];
export const Multi = component$(() => {
const selected = useSignal<string[]>(["ts", "rust"]);
const filterText = useSignal("");
const open = useSignal(false);
const remove$ = $((v: string) => {
selected.value = selected.value.filter((x) => x !== v);
});
return (
<Combobox.Root
multiple
bind:value={selected}
bind:open={open}
filter
placeholder="Add a language…"
onChange$={$(() => {
filterText.value = "";
})}
>
<Combobox.Label>Languages</Combobox.Label>
<Combobox.Control class={comboboxMultiselectControlClass}>
{selected.value.map((v) => (
<Combobox.Chip key={v} value={v} onRemove$={remove$}>
{LANGS.find((l) => l.value === v)?.label ?? v}
</Combobox.Chip>
))}
<Combobox.Input
bind:value={filterText}
class={comboboxMultiselectInputClass}
onFocus$={$(() => {
open.value = true;
})}
/>
</Combobox.Control>
<Combobox.Popover>
{LANGS.map(({ value, label }) => (
<Combobox.Item key={value} value={value}>
<Combobox.ItemLabel>{label}</Combobox.ItemLabel>
<Combobox.ItemIndicator>
<svg class="h-3.5 w-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M20 6L9 17l-5-5" />
</svg>
</Combobox.ItemIndicator>
</Combobox.Item>
))}
<Combobox.Empty>No languages.</Combobox.Empty>
</Combobox.Popover>
</Combobox.Root>
);
});
showOnFocus — open on focus
The showOnFocus prop on Combobox.Input opens the list automatically on focus without having to type.
import { Combobox } from "~/components/ui/base/combobox";
<Combobox.Root filter placeholder="Select or type…">
<Combobox.Label>Selection</Combobox.Label>
<Combobox.Control>
<Combobox.Input showOnFocus />
<Combobox.Trigger>▼</Combobox.Trigger>
</Combobox.Control>
<Combobox.Popover>
<Combobox.Item value="qwik">
<Combobox.ItemLabel>Qwik</Combobox.ItemLabel>
</Combobox.Item>
<Combobox.Item value="react">
<Combobox.ItemLabel>React</Combobox.ItemLabel>
</Combobox.Item>
<Combobox.Item value="vue">
<Combobox.ItemLabel>Vue</Combobox.ItemLabel>
</Combobox.Item>
<Combobox.Empty>No match.</Combobox.Empty>
</Combobox.Popover>
</Combobox.Root>
Metadata
Source: meta.generated.json (see npm run generate)
File base/combobox/meta.generated.json not found or path not allowed.