Auto Complete
Searchable text input with local and asynchronous suggestions.
AutoComplete combines an editable input with a listbox of suggestions. It can search a local collection, load results asynchronously, accept a custom value, and render rich options with icons, images, descriptions, and groups.
Basic usage
Pass options with a stable value and a visible label:
import { AutoComplete } from '@mosmox/ui';
const projects = [
{ value: 'mosmox-ui', label: 'Mosmox UI', description: 'Component library' },
{ value: 'website', label: 'Marketing website' },
];
<AutoComplete label="Project" placeholder="Type to search projects" options={projects} />This is an editable text input, not a Select trigger. By default, the suggestion popup opens after the query reaches minChars; set openOnFocus when a predefined local options list should be shown immediately after clicking the input. The list then filters as the user types. The input text becomes the selected option label after a suggestion is chosen. Selecting an option calls onChange with its value and option object.
Controlled and uncontrolled values
Use defaultValue for an internally managed field. For a controlled field, update the text through onInputChange and update the selected value through onChange:
const [project, setProject] = useState('');
<AutoComplete
label="Project"
value={project}
onInputChange={setProject}
onChange={(value) => setProject(value)}
options={projects}
/>onInputChange reports every text edit, including text that has not yet become a selected option. onOpenChange reports popup visibility changes.
Use onEnter for an action that must run whenever the user presses Enter. It receives the current input text, the active suggestion when one is highlighted, and the keyboard event. It runs whether or not a suggestion was selected:
<AutoComplete
label="Search"
options={projects}
onEnter={(query, option, event) => {
submitSearch(query, option?.value);
}}
/>When an active option exists, the component also selects it using the normal onChange flow before calling onEnter. Use allowCustomValue when Enter should commit text that is not present in the suggestions.
If onEnter is not provided, Enter performs only the normal selection behavior. Providing onEnter changes this into “select and run an application-defined action” without replacing the built-in selection.
Local filtering
By default, local options are matched case-insensitively against their label, description, and keywords. Add keywords for alternate search terms:
const countries = [
{ value: 'ua', label: 'Ukraine', keywords: ['ukr', 'europe'] },
{ value: 'us', label: 'United States', keywords: ['usa', 'america'] },
];For custom matching, provide filterOption:
<AutoComplete
options={countries}
filterOption={(option, query) => option.label.toString().startsWith(query)}
/>Remote search
Use loadOptions when suggestions come from an API. It receives the current query and an AbortSignal; cancel the request when the signal is aborted. Requests are debounced by searchDebounceMs (300ms by default), and minChars prevents requests for short queries. Remote mode waits for the query to reach minChars; openOnFocus does not request remote results without a query.
<AutoComplete
label="People"
placeholder="Search people"
minChars={2}
searchDebounceMs={400}
loadOptions={async (query, signal) => {
const response = await fetch(`/api/people?q=${encodeURIComponent(query)}`, { signal });
return response.json();
}}
/>Use loading for externally managed loading state. loadingText, emptyText, and noResultsText customize the status messages.
When loadOptions is provided, the returned options replace the local options list for that query. Previous requests are aborted before a new debounced request starts. The input remains editable while an asynchronous request is loading.
Rich options and custom rendering
An option may include an icon, image, description, group, disabled state, and search keywords:
| Property | Type | Description |
|---|---|---|
value | string | Submitted and selected value. |
label | ReactNode | Visible label and default input text. |
description | ReactNode | Supporting text below the label. |
icon | ReactNode | Decorative leading icon. |
image | string | Image URL rendered before the label. |
group | string | Group heading for the option. |
keywords | string[] | Additional terms used by local filtering. |
disabled | boolean | Prevents selecting the option. |
Use renderOption when the default layout is not enough:
<AutoComplete
options={projects}
renderOption={(option, state) => (
<span data-active={state.active}>
<strong>{option.label}</strong>
<small>{option.description}</small>
</span>
)}
/>The renderer receives active for keyboard or pointer highlight and selected for future selection-aware layouts. Auto Complete is single-value, so selected is currently false.
Sizes, variants, and adornments
size supports default, sm, and xs. The popup option typography and row height follow the input size. variant="filled" uses the shared filled control surface. Add leadingIcon or trailingIcon for contextual affordances. Auto Complete has no built-in Select chevron; its built-in action is the optional clear button.
<AutoComplete size="sm" leadingIcon={<Search />} options={projects} />
<AutoComplete size="xs" variant="filled" options={projects} />States and custom values
clearableadds a clear action when text is present.allowCustomValuelets Enter commit text that is not in the suggestions.errorrenders validation styling and supporting error text.disabledprevents focus and interaction.readOnlykeeps the value visible without allowing edits.fullWidthmakes the field fill its parent.nameadds a hidden native input for form submission.
Keyboard and accessibility
The input exposes the combobox role and controls a listbox. Use Arrow Down/Up to move through suggestions, Home/End to jump, Enter to select, and Escape to close. Disabled options cannot be selected. The visible label is associated with the input through htmlFor; description, error, and a supplied aria-describedby are combined into the input description. Provide aria-label when no visible label is present. Focus restoration uses preventScroll so opening or closing suggestions does not move the page.
API Reference
| Property | Type | Default | Description |
|---|---|---|---|
options | AutoCompleteOption[] | [] | Local suggestions. |
loadOptions | (query, signal) => Promise<AutoCompleteOption[]> | — | Async suggestion loader. |
value | string | — | Controlled selected value. |
defaultValue | string | '' | Initial uncontrolled value. |
onChange | (value, option) => void | — | Called after selecting or clearing. |
onEnter | (value, option, event) => void | — | Called for every Enter press, with the current text, active option, and keyboard event. |
onInputChange | (value) => void | — | Called on every text edit. |
open | boolean | — | Controlled popup state. |
onOpenChange | (open) => void | — | Popup state callback. |
openOnFocus | boolean | false | Shows a local options list when the input receives focus before typing. |
filterOption | (option, query) => boolean | built-in | Local filtering function. |
minChars | number | 1 | Minimum query length before suggestions open and remote search runs. |
searchDebounceMs | number | 300 | Remote search debounce delay. |
allowCustomValue | boolean | false | Commit values outside the option list. |
clearable | boolean | true | Show the clear action when applicable. |
size | default | sm | xs | default | Control and popup size. |
variant | outline | filled | outline | Control surface variant. |
label | ReactNode | — | Visible field label. |
description | string | — | Supporting text. |
error | string | — | Validation message. |
placeholder | string | Search | Empty input hint. |
leadingIcon | ReactNode | — | Leading decorative content. |
trailingIcon | ReactNode | — | Trailing decorative content. |
renderOption | (option, state) => ReactNode | built-in | Custom suggestion renderer. |
loading | boolean | false | Shows loading state. |
disabled | boolean | false | Disables the field. |
readOnly | boolean | false | Prevents editing while preserving focusability. |
name | string | — | Native form field name. |
id | string | generated | Input and listbox identifier base. |
className | string | — | Additional class on the field wrapper. |
fullWidth | boolean | false | Makes the field fill its parent. |
emptyText | ReactNode | Start typing to search | Status shown when a controlled popup is open below minChars. |
loadingText | ReactNode | Loading… | Status shown while loading. |
noResultsText | ReactNode | No results found | Status shown after a query has no matches. |
aria-label | string | — | Accessible name when there is no visible label. |
aria-describedby | string | — | Additional description element IDs. |
Theme tokens
Auto Complete uses the shared Input and Select token system rather than component-specific colors. The most relevant global tokens are:
| Token | Purpose |
|---|---|
--mm-color-text | Label and option text. |
--mm-color-muted-text | Descriptions and secondary content. |
--mm-color-placeholder | Empty input text. |
--mm-color-surface | Popup and option surface. |
--mm-color-control-selected | Active suggestion background, matching the Button.Light surface by default. |
--mm-color-control-filled | Filled trigger background. |
--mm-color-control-border | Trigger and popup borders. |
--mm-color-focus | Keyboard focus ring. |
--mm-color-danger-text | Error text. |
--mm-radius-control | Trigger and popup radius. |
--mm-size-icon | Icon sizing baseline. |
Override these through the same ConfigProvider theme used by the other form controls so the component follows the application-wide theme.