Mosmox UI
Current version
GitHubnpmjs.com
Forms & Inputs

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)}
/>

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:

PropertyTypeDescription
valuestringSubmitted and selected value.
labelReactNodeVisible label and default input text.
descriptionReactNodeSupporting text below the label.
iconReactNodeDecorative leading icon.
imagestringImage URL rendered before the label.
groupstringGroup heading for the option.
keywordsstring[]Additional terms used by local filtering.
disabledbooleanPrevents 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

  • clearable adds a clear action when text is present.
  • allowCustomValue lets Enter commit text that is not in the suggestions.
  • error renders validation styling and supporting error text.
  • disabled prevents focus and interaction.
  • readOnly keeps the value visible without allowing edits.
  • fullWidth makes the field fill its parent.
  • name adds 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

PropertyTypeDefaultDescription
optionsAutoCompleteOption[][]Local suggestions.
loadOptions(query, signal) => Promise<AutoCompleteOption[]>—Async suggestion loader.
valuestring—Controlled selected value.
defaultValuestring''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.
openboolean—Controlled popup state.
onOpenChange(open) => void—Popup state callback.
openOnFocusbooleanfalseShows a local options list when the input receives focus before typing.
filterOption(option, query) => booleanbuilt-inLocal filtering function.
minCharsnumber1Minimum query length before suggestions open and remote search runs.
searchDebounceMsnumber300Remote search debounce delay.
allowCustomValuebooleanfalseCommit values outside the option list.
clearablebooleantrueShow the clear action when applicable.
sizedefault | sm | xsdefaultControl and popup size.
variantoutline | filledoutlineControl surface variant.
labelReactNode—Visible field label.
descriptionstring—Supporting text.
errorstring—Validation message.
placeholderstringSearchEmpty input hint.
leadingIconReactNode—Leading decorative content.
trailingIconReactNode—Trailing decorative content.
renderOption(option, state) => ReactNodebuilt-inCustom suggestion renderer.
loadingbooleanfalseShows loading state.
disabledbooleanfalseDisables the field.
readOnlybooleanfalsePrevents editing while preserving focusability.
namestring—Native form field name.
idstringgeneratedInput and listbox identifier base.
classNamestring—Additional class on the field wrapper.
fullWidthbooleanfalseMakes the field fill its parent.
emptyTextReactNodeStart typing to searchStatus shown when a controlled popup is open below minChars.
loadingTextReactNodeLoading…Status shown while loading.
noResultsTextReactNodeNo results foundStatus shown after a query has no matches.
aria-labelstring—Accessible name when there is no visible label.
aria-describedbystring—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:

TokenPurpose
--mm-color-textLabel and option text.
--mm-color-muted-textDescriptions and secondary content.
--mm-color-placeholderEmpty input text.
--mm-color-surfacePopup and option surface.
--mm-color-control-selectedActive suggestion background, matching the Button.Light surface by default.
--mm-color-control-filledFilled trigger background.
--mm-color-control-borderTrigger and popup borders.
--mm-color-focusKeyboard focus ring.
--mm-color-danger-textError text.
--mm-radius-controlTrigger and popup radius.
--mm-size-iconIcon sizing baseline.

Override these through the same ConfigProvider theme used by the other form controls so the component follows the application-wide theme.