Combobox

Filters a list of options as the user types.

import { Combobox } from "@usefragments/ui";Source

Preview

<StatefulCombobox placeholder="Select a fruit">
  <Combobox.Input />
  <Combobox.Content>
    <Combobox.Item value="apple">Apple</Combobox.Item>
    <Combobox.Item value="banana">Banana</Combobox.Item>
    <Combobox.Item value="orange">Orange</Combobox.Item>

Usage

Use when

  • Lists too long to scan by eye
  • The user roughly knows the value they want
  • Autocomplete or typeahead
  • Multi-select from a searchable list

Avoid when

  • Under five options (use Select or RadioGroup)
  • Free-form text with no known options (use Input)
  • Short list nobody needs to search (use Select)
  • Actions rather than values (use Menu)

Implementation notes

  • Wrap in a Field for the label, description and error
  • Placeholder names what is searchable
  • With no match the list names the query; override it with Combobox.Empty when you have better wording
  • Set loading while options load from a server; the list says so
  • Group related options with Combobox.Group in long lists
  • Filtering keeps wrapper composition, so groups and fragments survive
  • Keep option text short and searchable
  • multiple turns selections into removable chips

Installation

pnpm add @usefragments/ui

The npm package stays managed by your package manager. Import styles once from @usefragments/ui/styles. See the installation guide for framework and theme setup.

Anatomy

Import Combobox once, then compose its parts with dot notation.

  • Combobox.Input
  • Combobox.Trigger
  • Combobox.Content
  • Combobox.Item
  • Combobox.ItemIndicator
  • Combobox.Empty
  • Combobox.Group
  • Combobox.GroupLabel

Type to narrow the list; unmatched queries show a no-results row.

<StatefulCombobox placeholder="Select a fruit">
  <Combobox.Input />
  <Combobox.Content>
    <Combobox.Item value="apple">Apple</Combobox.Item>
    <Combobox.Item value="banana">Banana</Combobox.Item>
    <Combobox.Item value="orange">Orange</Combobox.Item>
    <Combobox.Item value="grape">Grape</Combobox.Item>
  </Combobox.Content>
</StatefulCombobox>

Examples

Multiple Selection

Each pick becomes a removable chip in the field.

<StatefulCombobox multiple placeholder="Select fruits…">
  <Combobox.Input />
  <Combobox.Content>
    <Combobox.Item value="apple">Apple</Combobox.Item>
    <Combobox.Item value="banana">Banana</Combobox.Item>
    <Combobox.Item value="orange">Orange</Combobox.Item>

In a Field

Field gives the input its label and description.

<Field>
  <Field.Label>Assignee</Field.Label>
  <StatefulCombobox placeholder="Search assignees…">
    <Combobox.Input />
    <Combobox.Content>
      <Combobox.Item value="alice">Alice Johnson</Combobox.Item>

Invalid

The danger edge with the reason under the field.

<Field invalid>
  <Field.Label>Reviewer</Field.Label>
  <Combobox placeholder="Search reviewers…" invalid>
    <Combobox.Input />
    <Combobox.Content>
      <Combobox.Item value="alice">Alice</Combobox.Item>

With Groups

Headed sections stay intact while filtering.

<StatefulCombobox placeholder="Search countries…">
  <Combobox.Input />
  <Combobox.Content>
    <>
      <Combobox.Group>
        <Combobox.GroupLabel>North America</Combobox.GroupLabel>

With Empty State

Combobox.Empty replaces the default no-results wording.

<StatefulCombobox placeholder="Filter languages…">
  <Combobox.Input />
  <Combobox.Content>
    <Combobox.Empty>No language matches that search</Combobox.Empty>
    <Combobox.Item value="js">JavaScript</Combobox.Item>
    <Combobox.Item value="ts">TypeScript</Combobox.Item>

Loading

While options load, the list says Searching… instead of no match.

const repositories = ["fragments/web", "fragments/api", "fragments/docs"];

<Combobox loading placeholder="Search repositories…" defaultOpen>
  <Combobox.Input aria-label="Repository" />
  <Combobox.Content>
    {repositories.map((repo) => (

Scrollable List

Caps at four rows and half-shows the fifth as a scroll hint.

<StatefulCombobox placeholder="Search languages…">
  <Combobox.Input />
  <Combobox.Content>
    <Combobox.Empty>No results found</Combobox.Empty>
    <Combobox.Item value="js">JavaScript</Combobox.Item>
    <Combobox.Item value="ts">TypeScript</Combobox.Item>

Custom Max Visible Items

maxVisibleItems raises the cap to six rows.

<StatefulCombobox placeholder="Search cities…">
  <Combobox.Input />
  <Combobox.Content maxVisibleItems={6}>
    <Combobox.Empty>No results found</Combobox.Empty>
    <Combobox.Item value="nyc">New York</Combobox.Item>
    <Combobox.Item value="lon">London</Combobox.Item>

Disabled

Dimmed and unfocusable.

<Combobox disabled placeholder="Search…">
  <Combobox.Input />
  <Combobox.Content>
    <Combobox.Item value="1">Option 1</Combobox.Item>
  </Combobox.Content>
</Combobox>

Explicit Trigger

showTrigger={false} hands the open control to your own Combobox.Trigger.

<StatefulCombobox placeholder="Search assignees…">
  <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
    <Combobox.Input showTrigger={false} />
    <Combobox.Trigger aria-label="Open assignee list" />
  </div>
  <Combobox.Content>

API reference

Component props
Prop
Type
Default
Description
autoCompletestring | undefinedNot set—
autoHighlightboolean | undefinedtrueAuto-highlight first matching item while filtering
childrenRequiredReact.ReactNodeNot setCombobox input and content
Content.align"center" | "start" | "end" | undefinedcenterstartendNot set—
Content.childrenRequiredReact.ReactNodeNot set—
Content.maxVisibleItemsnumber | undefinedNot setMaximum number of visible options before scrolling. Shows half of the next item as a scroll hint.
Content.sideOffsetnumber | undefinedNot set—
defaultOpenboolean | undefinedfalseInitial open state for uncontrolled usage
defaultValuestring | string[] | undefinedNot setDefault selected value (uncontrolled)
disabledboolean | undefinedfalseDisable the combobox
Empty.childrenRequiredReact.ReactNodeNot set—
formstring | undefinedNot set—
Group.childrenRequiredReact.ReactNodeNot set—
GroupLabel.childrenRequiredReact.ReactNodeNot set—
Input.classNamestring | undefinedNot set—
Input.showTriggerboolean | undefinedNot setRender the built-in chevron trigger beside the input. Automatically disabled when an explicit <Combobox.Trigger /> is mounted.
inputRefReact.Ref<HTMLInputElement> | undefinedNot set—
invalidboolean | undefinedfalseThe danger edge and aria-invalid; say why in a Field.Error
Item.childrenRequiredReact.ReactNodeNot set—
Item.disabledboolean | undefinedNot set—
Item.valueRequiredstringNot set—
loadingboolean | undefinedfalseOptions are on their way: the list says Searching… and is aria-busy
multipleboolean | undefinedfalseAllow multiple selections with chips
namestring | undefinedNot setForm field name
onInputValueChange((query: string) => void) | undefinedNot setCalled with the typed query as it changes
onOpenChange((open: boolean) => void) | undefinedNot setCalled when dropdown open state changes
onValueChange((value: string | null) => void) | ((value: string[]) => void) | undefinedNot setCalled when selection changes
openboolean | undefinedNot setControlled open state of the dropdown
placeholderstring | undefinedNot setPlaceholder text for the input
readOnlyboolean | undefinedfalseShows the value on a dashed edge without letting it change
requiredboolean | undefinedNot setMarks the field required for form validation
sizeComboboxSize | undefinedxssmmdlg"md"Field height on the shared control track: 24, 28, 32 or 40
Trigger.childrenReact.ReactNodeNot set—
Trigger.classNamestring | undefinedNot set—
valuestring | string[] | null | undefinedNot setControlled selected value (string for single, string[] for multiple)

Accessibility

  • Arrow keys, Enter, and Escape all work
  • Typing filters the list in place
  • Options sit inside a listbox; Field.Label names the input
  • The empty and loading rows are polite status messages
  • The open button is named Show options and every chip remove button names its chip
  • SelectalternativeUse Select when search/filtering is not needed
  • InputsiblingUse Input for free-form text entry
  • ListboxsiblingUse Listbox for inline option lists

Next steps