Easy Shadcn
Components

Combobox

Combobox is an always-searchable, single-select autocomplete. It is a thin, opinionated preset over Select: searchable is forced on, multiple is unavailable, and the API is narrowed to the canonical combobox surface — an input you type into and a popover you pick from.

Uncontrolled — type to filter, click to select.
Controlled + clearable — selected: banana
Custom filter — matches the start of the label only.
Disabled.

Installation

With the @easy-shadcn namespace configured:

pnpm dlx shadcn@latest add @easy-shadcn/combobox

Or install via the full URL (zero configuration):

pnpm dlx shadcn@latest add https://easy-shadcn.vercel.app/r/combobox.json

Props

PropTypeDefaultDescription
itemsComboboxItem[]-Option list — see the table below. An empty array renders the empty state.
valuestring-Controlled value. Prop presence (even undefined) signals controlled mode.
defaultValuestring-Uncontrolled initial value.
onValueChange(value: string | undefined) => void-Called when the selection changes. undefined means no selection.
openboolean-Controlled popover open state.
defaultOpenbooleanfalseUncontrolled initial open state.
onOpenChange(open: boolean) => void-Called when the popover open state changes.
placeholderstring"Pick an option"Shown when no value is selected.
emptyMessageReactNode"No results"Shown when no items match the query (or items is empty).
clearablebooleanfalseShow an inline clear button when the field has a value.
filter(item: ComboboxItem, query: string) => booleancontainsCustom match predicate — overrides the default case-insensitive substring match.
disabledbooleanfalseDisables all interaction.
namestring-Native form field name — emits a hidden input carrying the selected value.
idstring-Applied to the input, for <label htmlFor> / the Field component.
aria-describedbystring-Forwarded to the input.
aria-invalidboolean | "true" | "false" | …-Forwarded to the input for invalid-state styling.

Slot class overrides: className (the InputGroup wrapper), inputClassName (the <input>), contentClassName (the popover container), itemClassName (every item), and emptyClassName (the empty state). The prop set is closed — anything not listed here lives on Select or the primitives.

ComboboxItem

FieldTypeDescription
valuestringUnique identifier + controlled value. "" is a valid selection.
labelReactNodeDisplay label. Strings/numbers drive the default filter, else value.
disabledbooleanMakes the item unselectable.
itemClassNameClassValuePer-item class, merged after the root-level itemClassName.

Examples

Controlled

Pass value and onValueChange. Prop presence signals controlled mode, so the display only updates when you feed the new value back in.

const [value, setValue] = useState<string | undefined>();

<Combobox items={items} onValueChange={setValue} value={value} />;

Clearable

<Combobox clearable items={items} onValueChange={setValue} value={value} />

The clear button appears on hover / focus while a value is set and emits onValueChange(undefined).

Custom filter

<Combobox
  filter={(item, query) =>
    String(item.label).toLowerCase().startsWith(query.toLowerCase())
  }
  items={items}
/>

Form integration

<form onSubmit={handleSubmit}>
  <label htmlFor="fruit">Fruit</label>
  <Combobox id="fruit" items={items} name="fruit" />
</form>

name emits a hidden input carrying the selected value, so the field participates in native form submission and FormData.

Accessibility

  • The input carries role="combobox", aria-expanded, aria-controls, and aria-haspopup, wired to the popover by the underlying primitive.
  • Use id with a <label htmlFor> to name the field; forward aria-describedby / aria-invalid for hints and validation state.
  • The popover closes on Escape and outside interaction; there is no trigger button — the input is the only focusable control.

Notes

  • Combobox is exactly <Select searchable multiple={false} /> with a narrower, discoverable API. If you need a non-searchable dropdown, multi-select chips, async loading, grouped or heterogeneous items, use Select or compose the components/ui/combobox primitives directly.
  • value={undefined} shows the placeholder; value="" is a distinct, valid selection that shows the "" item's label.
  • Combobox delegates to Select rather than reimplementing the searchable single-select branch — see docs/adr/combobox.md for the rationale.

On this page