Inspector Combobox

About this component

Inspector Combobox

Settings row for a list too long to read: pressing it opens a width-matched list with a search field, the keyboard walking the results while focus stays in the field. Options carry groups, descriptions, swatches and search keywords, and `multiple` turns the row into a counted multi-select.

Usage

The import and the props worth knowing about, in one place.

import { InspectorCombobox } from "@/components/beste/component/inspector-combobox";

const pages = [
  { value: "home", label: "Home", group: "Pages" },
  { value: "pricing", label: "Pricing", group: "Pages" },
  { value: "docs", label: "Documentation", group: "Pages", keywords: ["help", "guide"] },
  { value: "cart", label: "Cart", group: "Shop" },
];

<InspectorCombobox label="Link to" options={pages} defaultValue="pricing" />

// Controlled. The argument is a string here and an array under `multiple`, so
// narrow it at the call site, which is what knows which it asked for.
<InspectorCombobox
  label="Link to"
  options={pages}
  value={page}
  onValueChange={(next) => setPage(next as string)}
  onOpenChange={(open) => console.log("list open:", open)}
  searchable                       // on by default past eight options
  searchPlaceholder="Find a page"
  placeholder="Choose a page"      // shown while nothing is selected
  emptyMessage="No page matches"
  clearable                        // adds a "Clear Link to" button at the foot
  tone="outline"                   // "muted" (default) | "outline" | "ghost"
  size="sm"                        // "sm" | "default" | "lg"
/>

// Several at once: the row counts them, and the list stays open between presses
<InspectorCombobox
  label="Categories"
  multiple
  max={3}
  options={["Editorial", "Commerce", "Marketing", "Docs"]}
  value={categories}
  onValueChange={(next) => setCategories(next as string[])}
/>

// Options can carry a mark and a second line
<InspectorCombobox
  label="Theme"
  options={[
    { value: "sand", label: "Sand", swatch: "#d6cbb8", description: "Warm neutral" },
    { value: "slate", label: "Slate", swatch: "#64748b", description: "Cool neutral" },
  ]}
/>

Keyboard and gestures

Every one of these is in the component already. They are listed because a props table cannot mention a gesture, so nothing else on this page can tell you they exist.

Down / UpMove through the results without leaving the search field
Home / EndJump to the first or last result
EnterChoose the active result
EscapeClose the list, leaving the value alone
TypingFilters on the label, the value, the description and any keywords

Props

Read from the component's own type, so this cannot drift from what it accepts.

PropTypeDefaultDescription
label*string—Label rendered on the left, inside the row.
iconLucideIcon—Optional leading icon shown before the label.
options(string | InspectorComboboxOption)[]—The options on offer.
valuestring | string[]—Controlled selection: a string, or an array of them with `multiple`. Pair it with `onValueChange`.
defaultValuestring | string[]—Initial selection in uncontrolled mode.
onValueChange(value: ComboboxValue) => void—Fires on every selection. The argument is a string, or an array of them when `multiple` is set — narrow it at the call site, which knows which it asked for.
onOpenChange(open: boolean) => void—Fires when the list opens or closes.
multipleboolean—Choose several. The row then counts them and the list keeps itself open.
maxnumber—Ceiling on how many may be chosen at once, with `multiple`.
searchableboolean—Show the search field. Defaults to on once there are more than eight options, which is the point at which reading the list stops being faster than typing.
searchPlaceholderstring—Ghost text for the search field.
placeholderstring—Shown in the row while nothing is selected.
emptyMessagestring—Message shown when nothing matches the search.
clearableboolean—Offer a clear button at the foot of the list once something is selected.
disabledboolean—Block interaction and dim the row.
tone"muted" | "outline" | "ghost""muted"Surface treatment: filled (default), hairline outline, or bare until hover.
size"sm" | "default" | "lg""default"Row height preset.
classNamestring—
aria-labelstring—Accessible name. Falls back to `label`.

More Inspector components

View all Inspector
Dark Mode