Combobox

Searchable text input with a dropdown of filterable options; use for long lists where a plain select is awkward

A combobox built on Ark UI Combobox. The default <Combobox /> wires filtering, collection, and list UI for you. For full control over markup or data flow, use the compound parts (ComboboxRoot, ComboboxInput, etc.).

import { Combobox } from "@/components/ui/combobox";

export default function Example() {
  return (
    <Combobox
      label="Framework"
      placeholder="Search framework..."
      items={[
        { label: "React", value: "react" },
        { label: "Vue", value: "vue" },
        { label: "Svelte", value: "svelte" },
      ]}
    />
  );
}

Installation

bash
liminal add combobox

Usage

Pass items as an array of { label, value, disabled? }. The input filters options with case-insensitive matching. Use emptyText when no item matches the query.

import { Combobox } from "@/components/ui/combobox";

export default function Example() {
  return (
    <Combobox
      label="Framework"
      placeholder="Search framework..."
      emptyText="No matches."
      items={[
        { label: "React", value: "react" },
        { label: "Vue", value: "vue", disabled: true },
        { label: "Svelte", value: "svelte" },
      ]}
    />
  );
}

Examples

Compound API

Use ComboboxRoot with useListCollection and useFilter from Ark UI to mirror the convenience behavior, or customize collection and item rendering. Export also includes ComboboxLabel, ComboboxControl, ComboboxInput, ComboboxTrigger, ComboboxContent, ComboboxItem, and ComboboxEmpty.

tsx
import { useListCollection } from "@ark-ui/react/combobox";
import { useFilter } from "@ark-ui/react/locale";
import {
  ComboboxRoot,
  ComboboxLabel,
  ComboboxControl,
  ComboboxInput,
  ComboboxTrigger,
  ComboboxContent,
  ComboboxItem,
  ComboboxEmpty,
} from "@/components/ui/combobox";

type Item = { label: string; value: string };

const items: Item[] = [
  { label: "React", value: "react" },
  { label: "Vue", value: "vue" },
];

export default function Example() {
  const { contains } = useFilter({ sensitivity: "base" });
  const { collection, filter } = useListCollection({
    initialItems: items,
    itemToString: (item) => item.label,
    itemToValue: (item) => item.value,
    filter: contains,
  });

  return (
    <ComboboxRoot
      collection={collection}
      onInputValueChange={(d) => filter(d.inputValue)}
    >
      <ComboboxLabel>Framework</ComboboxLabel>
      <ComboboxControl>
        <ComboboxInput placeholder="Search..." />
        <ComboboxTrigger aria-label="Toggle combobox" />
      </ComboboxControl>
      <ComboboxContent>
        <ComboboxEmpty>No results found.</ComboboxEmpty>
        {collection.items.map((item) => (
          <ComboboxItem key={item.value} item={item}>
            {item.label}
          </ComboboxItem>
        ))}
      </ComboboxContent>
    </ComboboxRoot>
  );
}

For most cases, the convenience <Combobox items={...} /> is enough.

API

Props listed below are specific to the convenience component. The root also accepts Ark Combobox.Root props (for example value, onValueChange, selectionBehavior). See the Ark Combobox docs for the full API.

PropTypeDefaultDescription
items{ label: string; value: string; disabled?: boolean }[]-Options shown in the list (convenience API).
labelstring-Optional field label above the control.
placeholderstring"Search..."Placeholder for the input.
emptyTextstring"No results found."Message when the filtered list is empty.