build69
build69  /  Components  /  Selection & Choice  /  ComboboxAsyncMulti

ComboboxAsyncMulti

v1.0.22Updated 20 Oct 2026

Multi-select counterpart to ComboboxAsync: the same host-driven async loading, spinner, error-and-retry panel, creatable and free-solo rows and grouped results, but the trigger carries one removable chip per value with a "+N more" count past `maxChips`. `maxValues` caps how many can be picked, and Backspace or Delete on the trigger removes the last chip. Values the current result set no longer contains still render as chips using the raw value as the label.

Preview

Live · 6 stories
▸Why this is recommendedmulti-selectcomboboxautocompleteasync+6

ComboboxAsyncMulti handling multiple items at once.

Recommended for+14
  • ✓Tagging an invoice or ticket with customers fetched from the server
  • ✓Selecting multiple recipients from a user-search endpoint
  • ✓Attaching several products from a large remote catalogue
  • ✓Free-form tag entry capped at N tags
  • ✓Multi-value filters backed by a search API
Tags+6
multi-selectcomboboxautocompleteasyncserver-searchcreatablefree-solochipstagsmulti
Features+4
  • Host-driven async loading via onSearchChange + options
  • Chips in the trigger with a "+N more" overflow past maxChips
  • Hard cap on selections via maxValues
  • Backspace / Delete on the trigger removes the last chip
  • Creatable and free-solo rows that append to the value array
  • Chips keep their labels after the result set moves on — picked options are remembered, and `selectedOptions` covers values that were never in a result set
  • Every reset of the internal search is announced through `onSearchChange`, and `onOpenChange` reports open and close
  • Loading spinner and error panel with a Retry button
  • Grouped results with headings plus a meta line per option
  • Labelled by FormField: id and aria-* land on the role=combobox trigger, which then fills the field column
Not recommended for−2
  • ✕Selecting a single value (use ComboboxAsync)
  • ✕Static option sets already in memory (use MultiCombobox)
  • ✕Small fixed sets of 2-5 options (use Checkbox group)

Install

Ask your agent, or call the MCP tool directly. The resolved bundle is written into your project.

get_registry_item({ name: "ComboboxAsyncMulti" })

Usage

import { ComboboxAsyncMulti } from '@build69/ui';

<ComboboxAsyncMulti
  values={tags}
  onValuesChange={setTags}
  options={results}
  onSearchChange={debouncedSearch}
  loading={isLoading}
  freeSolo
  maxValues={3}
  maxChips={5}
  placeholder="Pick tags…"
/>

Tick variants in the left menu — each adds a tab with that variant’s real story source, the same code the preview above renders.

Props

PropTypeRequired
options

Current result set — the host replaces it in response to onSearchChange

ComboboxAsyncOption[]yes
values

Selected values (default [])

string[]—
onValuesChange

Fires with the next array on toggle, create, or chip removal

(values: string[]) => void—
selectedOptions

The options the current values stand for. A chip falls back to a label found here when its value is not in the current options — needed only for values the component has never seen, such as a form that mounts with saved values. Entries that are not selected are ignored

ComboboxAsyncOption[]—
onSearchChange

Fires on every keystroke; the host should debounce and fetch. Supplying it also turns off the built-in cmdk filter. Also fires with an empty string whenever the component clears its own search (after a selection, a create or a free-solo commit), so the host never keeps a query the user can no longer see

(query: string) => void | Promise<void>—
onOpenChange

Fires on every open and close of the popover, including the programmatic close after a selection — lets the host drop its query when the box closes without one

(open: boolean) => void—
maxChips

How many chips render inline before collapsing to "+N more" (default 3)

number—
maxValues

Cap on selections; further picks are ignored

number—
loading

Show the loading spinner in the list

boolean—
error

Error message; replaces the list with an error panel

string—
onRetry

Shows a Retry button inside the error panel

() => void—
creatable

Show a "Create '<query>'" row when no option matches exactly

boolean—
onCreate

Called with the query on create; the host owns value assignment. Without it, the query is appended to values

(query: string) => void—
freeSolo

Allow appending any typed text as a value

boolean—
disabled

Disable the trigger

boolean—
scrollbar

How the option list draws its scrollbar once it overflows — forwarded to CommandList. Default 'thin': a visible themed bar, because the height cap is the only thing hiding the remaining options

'thin' | 'hidden' | 'auto'—
listMaxHeight

Cap the option list's height (number = px, string = any CSS length such as '50vh'). Omitted, the list keeps CommandList's 18rem default

number | string—
id

DOM id set on the trigger — a <label htmlFor> (or FormField) names the control through it

string—
aria-label

Accessible name for the trigger when there is no visible label

string—
aria-labelledby

Id(s) of the visible label element(s) that name the trigger

string—
aria-describedby

Id(s) of hint or error text that describe the trigger

string—
aria-invalid

Mark the trigger invalid — FormField clones this in from its error prop

boolean—
aria-required

Mark the trigger required — FormField clones this in from its required prop

boolean—
invalid

Mark the field invalid (destructive ring); aria-invalid wins when both are set

boolean—
required

Mark the field required (aria-required on the trigger)

boolean—
className

Class applied to the trigger; a width here wins over the default (w-full inside a FormField, the standalone width otherwise)

string—

Related