Combobox
FreeA searchable select with grouped options and keywords.
pnpm dlx shadcn@latest add https://caliperui.site/r/combobox.jsonWhen to use #
Use it for
- More than ten options, or options people know by name.
- Regions, people, countries and assets.
Skip it when
- Fewer than seven options; use a select.
- More than one value is allowed; use a multi-select.
Installation #
terminal
pnpm dlx shadcn@latest add https://caliperui.site/r/combobox.json
Adds its 3 files to components/ui/, each with its own stylesheet, and installs their packages. No account needed. The first part also adds Caliper UI’s tokens; import them once as in Installation.
Usage #
This is the code behind the preview above.
example.tsx
import { useState } from "react" import { Combobox } from "@/components/ui/combobox" const people = [ { id: "mara", label: "Mara Chen", kind: "person" as const }, { id: "noor", label: "Noor Haddad", kind: "person" as const }, { id: "design", label: "design", kind: "channel" as const }, ]; export function RegionPicker() { const [person, setPerson] = useState("priya"); const [people, setPeople] = useState([ { value: "priya", label: "Priya Raman", group: "Admin" }, { value: "rafael", label: "Rafael Abreu", group: "Finance" }, { value: "kiara", label: "Kiara Murayama", group: "Ops" }, { value: "tomas", label: "Tomás Ferreira", group: "Editor" }, { value: "mara", label: "Mara Okafor", group: "Finance" }, ]); return ( <div className="w-full max-w-xs"> <Combobox label="Assign INV-0416 to" placeholder="Name, team or email" options={people} value={person} onValueChange={setPerson} createLabel={(query) => `Invite “${query}” to Halden Analytics`} onCreate={(query) => { const value = query.toLowerCase().replace(/\s+/g, "-"); setPeople((current) => [...current, { value, label: query, group: "Invited" }]); setPerson(value); }} /> </div> ); } const team = [ { value: "mara", label: "Mara Chen" }, { value: "noor", label: "Noor Haddad" }, { value: "ivo", label: "Ivo Petrov" }, { value: "sana", label: "Sana Okafor" }, ];
API reference #
Combobox
component
PropTypeDefaultDescription
label *string—
options *readonly ComboboxOption[]—
valuestring—
defaultValuestring""
onValueChange((value: string) => void)—
placeholderstring"Search"
namestring—
disabledboolean—
idstring—
classNamestring—
onCreate((query: string) => void)—Offered when nothing matches, e.g. invite someone by the typed name.
createLabel((query: string) => string)(query: string) => `Add “${query}”`
Accessibility #
It exposes combobox, listbox, status and option semantics, with aria-expanded, aria-controls, aria-autocomplete, aria-activedescendant, aria-hidden and aria-label kept in step with state. Changes that matter are announced through a live region.
KeyBehaviour
↓Moves to the next item or value.
↑Moves to the previous item or value.
EnterActivates the focused control.
EscapeCloses or cancels, then returns focus to where it came from.
Motion #
Transitions use the shared duration and easing tokens. Under prefers-reduced-motion those tokens collapse to instant and travel distances go to zero, so state changes still happen without movement.