Efniv0.1

Components

Combobox

A text input that filters a list as you type and commits a single value. Built on Base UI's Combobox, so the filtering, the typeahead and the ARIA wiring come from the primitive.

When to usewhen the list is long enough that scanning it beats scrolling it — operators, pads, mission IDs. For a short fixed list reach for Select; for a global action palette, Command.

Import

import {
  Combobox, ComboboxInput, ComboboxContent, ComboboxList, ComboboxItem, ComboboxEmpty,
} from "@ravnhq/efni/combobox"

Filtering as you type

ComboboxInput is the anchor and the filter. Pass the items to Combobox and the primitive matches them.

<Combobox items={OPERATORS}>
  <ComboboxInput placeholder="Search operators" />
  <ComboboxContent>
    <ComboboxEmpty>No operator matches.</ComboboxEmpty>
    <ComboboxList>
      {OPERATORS.map((operator) => (
        <ComboboxItem key={operator} value={operator}>{operator}</ComboboxItem>
      ))}
    </ComboboxList>
  </ComboboxContent>
</Combobox>

As a trigger

ComboboxTrigger shows the committed value instead of an open text field — closer to Select, but still filterable once open.

<Combobox items={pads} defaultValue="Pad 3">
  <ComboboxTrigger>
    <ComboboxValue placeholder="Pick a pad" />
  </ComboboxTrigger>
  <ComboboxContent>
    <ComboboxInput placeholder="Filter pads" />
    <ComboboxList>
      <ComboboxGroup>
        <ComboboxGroupLabel>Active</ComboboxGroupLabel>
        <ComboboxItem value="Pad 3">Pad 3</ComboboxItem>
      </ComboboxGroup>
    </ComboboxList>
  </ComboboxContent>
</Combobox>

Building it by hand

Before this component existed, this page documented a Popover + Command recipe. It is still the right answer when you need a command palette with your own filtering, grouping or async loading — Combobox owns its filter, that composition does not.

// Reach for this when you need control of the filtering itself.
<Popover>
  <PopoverTrigger render={<Button variant="outline">Pick an operator</Button>} />
  <PopoverContent>
    <Command>
      <CommandInput placeholder="Search operators" />
      <CommandList>
        <CommandEmpty>No results.</CommandEmpty>
        <CommandGroup>
          <CommandItem onSelect={pick}>OP-0142 · Alvarez</CommandItem>
        </CommandGroup>
      </CommandList>
    </Command>
  </PopoverContent>
</Popover>

Accessibility

KeyAction
↓ / ↑Move through the filtered items
EnterCommit the highlighted item
EscapeClose without committing
Home / EndJump to the first or last item
  • The primitive wires role="combobox", aria-expanded and aria-activedescendant; this wrapper only adds classes and slots.
  • Label the input — with Label and htmlFor, or an aria-label. A placeholder is not a label.
  • ComboboxSeparator is aria-hidden: ARIA does not allow a separator as a listbox child, and axe flags it.

Guidelines

Do
  • Give the input a real label; the placeholder disappears as soon as someone types.
  • Write an empty state that says what did not match, not just “No results”.
  • Group items when the list has natural sections, and label the groups.
Don't
  • Don't use Combobox for a list of three options — that is Select.
  • Don't build a global action palette with it; Command is the one with the dialog and the shortcut.
  • Don't filter the items yourself and pass the result — the primitive already filters, and doing both double-filters.

Props

PropTypeDefaultDescription
itemsreadonly any[] | readonly Group<any>[]—The items to be displayed in the list. Can be either a flat array of items or an array of groups with items.
valueComboboxValueType<Value, Multiple> | null—The selected value of the combobox. Use when controlled.
defaultValueComboboxValueType<Value, Multiple> | null—The uncontrolled selected value of the combobox when it's initially rendered. To render a controlled combobox, use the value prop instead.
onValueChange((value: ComboboxValueType<Value, Multiple> | (Multiple extends true ? never : null), eventDetails: ComboboxRoot.ChangeEventDetails) => void)—Event handler called when the selected value of the combobox changes.
openboolean—Whether the popup is currently open. Use when controlled.
defaultOpenbooleanfalseWhether the popup is initially open. To render a controlled popup, use the open prop instead.
onOpenChange((open: boolean, eventDetails: ComboboxRoot.ChangeEventDetails) => void)—Event handler called when the popup is opened or closed.
ComboboxContent.side"top" | "bottom" | "left" | "right" | "inline-end" | "inline-start""bottom"Which side of the anchor element to align the popup against. May automatically change to avoid collisions.
ComboboxContent.align"center" | "start" | "end""start"How to align the popup relative to the specified side.
filternull | ((itemValue: ItemValue, query: string, itemToString?: (itemValue: ItemValue) => string) => boolean)—Filter function used to match items vs input query.
itemToStringLabel((itemValue: Value) => string)—When the item values are objects (<Combobox.Item value={object}>), this function converts the object value to a string representation for display in the input.