Skip to content
Primitives

Combobox

Autocomplete select — a Command palette on a hard-shadow popover with a mono search and a signal check on the active option. Controlled via `value`/`onValueChange`, so it drops straight into a Form field.

● LIVE
npx shadcn@latest add https://okibi.cndr.dev/r/combobox.json

Installation

Install the theme first, then add the component:

npx shadcn@latest add https://okibi.cndr.dev/r/combobox.json

Install the required dependencies:

npm install lucide-react

Add the okibi building blocks it composes: button, command, popover.

Copy the source from the Code tab above into components/ui/combobox.tsx. It uses the cn helper and the okibi theme tokens — install the theme first if you haven't.

Usage

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

const [value, setValue] = React.useState("")

<Combobox
  options={[
    { value: "alpha", label: "Alpha" },
    { value: "bravo", label: "Bravo" },
  ]}
  value={value}
  onValueChange={setValue}
  placeholder="Select sector"
/>

Anatomy

// A single prop-driven component — it composes a Popover trigger over a
// Command palette internally, so there are no sub-parts to assemble.
<Combobox
  options={[{ value: "alpha", label: "Alpha" }]}  // value drives state, label drives display + search
  value={value}                                   // controlled selection
  onValueChange={setValue}                         // empty string clears
  placeholder="Select sector"                      // trigger text when empty
  searchPlaceholder="Search…"                      // command input placeholder
  emptyText="No results."                          // shown when nothing matches
  id="sector"                                       // pair with an external <Label htmlFor>
/>

Examples

Disabled

A disabled trigger locks the control on its committed value; individual options can be disabled too.

● LIVE

Long list

Over a long option set the command palette stays searchable and scrolls inside the popover.

● LIVE

API Reference

Combobox

PropTypeDefaultDescription
options{ value: string; label: string }[]The selectable options.
valuestringSelected option value (controlled).
onValueChange(value: string) => voidFires on select (empty string when cleared).
placeholderstring"Select option"Trigger text when empty.
searchPlaceholderstring"Search…"Placeholder for the search input.
emptyTextstring"No results."Shown when the search matches nothing.
disabledbooleanfalseDisable the trigger.

Accessibility

  • The trigger carries role='combobox' and aria-expanded, so its collapsed/expanded state is announced.
  • Built on the Command palette: type-ahead filtering, arrow-key navigation, Enter to select, and Esc to close all come for free.
  • Filtering matches on each option's label via keywords, so users search by what they see, not the underlying value.
  • The standalone trigger has no accessible name on its own — supply an id and an external Label htmlFor to name it.
  • The active option is marked with a CheckIcon; selection isn't conveyed by the icon alone, since the label text stays in the trigger.

Guidelines

Do

  • Give every option a human label and a stable value; the label drives both display and search.
  • Customize searchPlaceholder and emptyText to match the data set so the empty state reads naturally.
  • Override the fixed w-56 width through className when labels are long.

Don't

  • Don't expect a separate clear control — re-selecting the active option emits an empty string to clear it; document that for users.
  • Don't reach for it for free-text entry or multi-select; it's a single-select picker over a known option list.

On this page