SearchMultiCombobox
A searchable multiple choice from a list your application already has, summarised on one line in the button.
Use Search when the reader picks several items from a list that is easier to search
than to scan: events, languages, roles as a field in a form. It is the multiple form of
SearchCombobox, with the same identity and search. When
the reader should weigh a small closed set together, use checkboxes instead.
Value: ["statement.sent"]
// apps/docs/examples/ui/search-multi-combobox.tsx'use client';
import {useState} from 'react';
import {SearchMultiCombobox} from '@workspace/active-ui/components/composed/search-multi-combobox';import { Field, FieldDescription, FieldLabel,} from '@workspace/active-ui/components/primitives/field';
const EVENTS = [ { value: 'participant.created', label: 'Participant added', keywords: 'participant.created', }, { value: 'participant.updated', label: 'Participant changed', keywords: 'participant.updated', }, { value: 'statement.sent', label: 'Statement sent', keywords: 'statement.sent', }, { value: 'statement.failed', label: 'Statement failed', keywords: 'statement.failed', }, { value: 'import.finished', label: 'Import finished', keywords: 'import.finished', },];
/** * A searchable multiple choice. The menu stays open while you choose; the * closed button shows up to two labels and then the count. The technical * event keys are searchable but not shown. What an empty choice means is the * application's decision. */export default function SearchMultiComboboxExample() { const [events, setEvents] = useState<string[]>(['statement.sent']); return ( <Field className="w-full max-w-xs"> <FieldLabel htmlFor="webhook-events">Send a webhook on</FieldLabel> <SearchMultiCombobox id="webhook-events" options={EVENTS} value={events} onValueChange={setEvents} placeholder="Choose events" searchLabel="Search events" searchPlaceholder="Search…" emptyText="No event found." summaryText={(count) => (count === 1 ? '1 event' : `${count} events`)} clearLabel="Clear the choice" locale="en" aria-describedby="webhook-events-value" /> <FieldDescription id="webhook-events-value"> Value: <code>{JSON.stringify(events)}</code> </FieldDescription> </Field> );}What the composition adds#
One line in the button. Up to two labels, then your
summarywith the count. The button never wraps.Text The menu stays open while choosing, with the search field on top and a check mark on each chosen option, and the count at the bottom.
A stable order: chosen values keep their order, a new value is added at the end.
clearadds a clear action; without it there is none. Clearing empties the choice, it never means "all".Label Unknown values are kept. A stored value with no option in the list stays in the value and in the count; the component cannot show a label for it.
No chips, no "select all", no groups or tree, no free values and no server search.
Usage#
import {SearchMultiCombobox} from '@workspace/active-ui/components/composed/search-multi-combobox';
<SearchMultiCombobox id="events" options={events} value={chosen} // string[] onValueChange={setChosen} placeholder="Choose events" searchLabel="Search events" searchPlaceholder="Search…" emptyText="No event found." summaryText={(count) => `${count} events`} clearLabel="Clear the choice" locale="en"/>;What your application provides#
The list,
summaryas a function so your translation can handle the plural, and the other texts.Text What an empty choice means (none, all, inherit) and whether it is allowed.
Accessibility and keyboard#
Name the button with
FielditsLabel html For id.In the menu,
ArrowandUp Arrowmove,Down Entertoggles the active option, andEscapecloses.
API#
Import from @workspace/
Props
- Type
string
Text when the search returns nothing. Supplied translated.
- Type
string
The reader's language, for matching the query against labels. Required for the same reason as on Search: without it Base UI's collator uses the runtime locale.
- Type
readonly SearchMulti Combobox Optio…
The complete, locally available option list. The component filters itself; the caller does not filter as well.
Server-side search does not exist for this component: that is a different lifecycle (loading, failing, stale responses) and there is no consumer for a multiple remote variant today.
- Type
string
Text on the button while nothing is selected. Supplied translated.
- Type
string
Accessible name of the search field inside the menu. Supplied translated.
Required: the field has no visible label, and its placeholder is not a name. The caller owns the wording, so two pickers on one page can be told apart ("Search category", "Search participant").
- Type
string
Text in the search field inside the menu. Supplied translated.
- Type
(count: number) => string
The summary once the button no longer spells out the labels, and the line at the bottom of the menu.
A function and not a string, because it contains a number and active- neither interpolates nor translates: (n) => t('common..
- Type
readonly string[]
The selected identities. Empty is a valid state.
What empty means is the caller's call and never this component's: none, all, inherit or no filter are four different domain meanings.
- Type
string
The label of the clear action. If it is missing, there is no clear action.
Clearing means only: the explicit set is empty. It never switches to "all", and whether empty is allowed is decided by the caller's validation.
Events
Called by the component when something happens.
- Type
(value: string[]) => void
Receives the new set of identities.
The order is stable and documented: existing values keep their relative position, a new value is appended. That is not a domain ordering (membership is unordered), but it prevents a value from changing shape on every click.
Other props, such as the standard attributes of the underlying element, className and ref, are passed through.
Types
interface SearchMultiComboboxOption {
value: string;
label: string;
disabled?: boolean;
keywords?: string;
}The props of Search, each described in its prop list on this page.
interface SearchMultiComboboxProps extends Omit<
React.ComponentProps<'button'>,
'value' | 'defaultValue' | 'onChange' | 'children'
> {
options: readonly SearchMultiComboboxOption[];
value: readonly string[];
onValueChange: (value: string[]) => void;
placeholder: string;
searchLabel: string;
searchPlaceholder: string;
emptyText: string;
summaryText: (count: number) => string;
clearLabel?: string;
locale: LocaleCode;
}