Skip to main content

Autocomplete

A Selector, with an input filtering options. Only provided options can be returned as the result (another value typed in the input cannot be returned).

Usage

import { Autocomplete } from '@easy-ui-react/easy-ui-react'

const fruitOptions = [
{ value: 'apple', label: 'Apple' },
{ value: 'banana', label: 'Banana' },
{ value: 'cherry', label: 'Cherry' },
]

export function Example() {
const [value, setValue] = useState<string>()
return <Autocomplete options={fruitOptions} value={value} onValueChange={setValue} placeholder="Search a fruit..." />
}
Live Editor
<Autocomplete
  color="primary"
  placeholder="Search a fruit..."
  options={[
    { value: 'apple', label: 'Apple' },
    { value: 'banana', label: 'Banana' },
    { value: 'cherry', label: 'Cherry' },
  ]}
/>
Result
Loading...

Variants

VariantDescription
borderedFull border, transparent background (default)
fadedFull border, tinted background
flatTinted background, no border
underlinedBottom border only, no radius
Live Editor
<Autocomplete
  variant="bordered"
  placeholder="Bordered"
  options={[{ value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }]}
/>
Result
Loading...

Colors

  • default
  • primary
  • secondary
  • success
  • warning
  • error

Variant affects colors. Have a look to Storybook to see how the component is rendering depending on the variant.

Live Editor
<Autocomplete
  color="primary"
  placeholder="Primary"
  options={[{ value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }]}
/>
Result
Loading...

Sizes

  • sm
  • md
  • lg
Live Editor
<Autocomplete
  size="sm"
  placeholder="Small"
  options={[{ value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }]}
/>
Result
Loading...

Radius

  • none
  • sm
  • md
  • lg
  • full
Live Editor
<Autocomplete
  radius="sm"
  placeholder="Rounded"
  options={[{ value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }]}
/>
Result
Loading...

sm, md and lg map to --easyui-radius-* CSS variables, see the theming guide.

The underlined variant always uses rounded-none regardless of the radius prop.

Content

Add an icon (or any node) before/after the input with startContent / endContent. By default, they sit inside the field. Set startContentPlacement / endContentPlacement to outside to render them outside it.

Live Editor
<Autocomplete
  startContent={<Apple size={16} />}
  placeholder="Search a fruit..."
  options={[{ value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }]}
/>
Result
Loading...

Arrow

A ChevronDown icon is shown by default at the end of the field, and rotates when the listbox opens. Clicking it focuses the input and opens the listbox, same as clicking anywhere else in the field.

  • isArrowHidden removes it entirely
  • arrowPlacement moves it to the start or end (default) of the field
  • arrow replaces the default icon with any node
Live Editor
<Autocomplete
  arrowPlacement="start"
  placeholder="Arrow on the left"
  options={[{ value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }]}
/>
Result
Loading...

Filtering

Typing narrows options to a case-insensitive substring match against each option's label, not just a prefix match, so typing "an" matches Banana (the match can be anywhere in the label).

Only an option selected from the list can be committed as the value. Typed text is never committed directly. On blur or Escape, the input reverts to the committed option's label (or clears if nothing is selected).

When no option matches the typed text, the listbox shows a customizable "No results found" message instead of an empty list.

Live Editor
<Autocomplete
  placeholder={'Try typing "an"...'}
  options={[
    { value: 'apple', label: 'Apple' },
    { value: 'banana', label: 'Banana' },
    { value: 'mango', label: 'Mango' },
    { value: 'cherry', label: 'Cherry' },
  ]}
/>
Result
Loading...

Clear on focus

By default, focusing a field that already has a value shows the committed option's label, so the first keystroke appends to it. Set isInputClearedOnFocus to empty the input instead, letting the user type a new search and refreshing the options.

Only the displayed text is cleared, the committed value is untouched:

  • selecting another option commits it, as usual.
  • leaving the field (blur, Escape, or a click outside) without selecting anything brings the previous value back.

This behavior also applies if the Autocomplete input is focused

Live Editor
<Autocomplete
  label="Fruit"
  defaultValue="apple"
  isInputClearedOnFocus
  placeholder="Search a fruit..."
  options={[
    { value: 'apple', label: 'Apple' },
    { value: 'banana', label: 'Banana' },
    { value: 'mango', label: 'Mango' },
  ]}
/>
Result
Loading...

The prop has no visible effect with selectionMode="multiple", where the input is already emptied after each selection and the values are carried by the chips.

Set it once for the whole app with defaults.autocomplete.isInputClearedOnFocus.

Selection mode

selectionMode switches between selecting one value ('single', the default) and several ('multiple'). It also switches the type of value, defaultValue and onValueChange:

selectionModevalue / defaultValueonValueChange
'single'string(value: string) => void
'multiple'string[](values: string[]) => void

In multiple mode, the selected values are shown as removable chips inside the field, the input keeps the remaining space, and its text is cleared after each selection so you can chain them without the listbox closing. Clicking an already selected option deselects it, and Backspace on an empty input removes the last selected value.

const [values, setValues] = useState<string[]>([])

<Autocomplete selectionMode="multiple" options={fruitOptions} value={values} onValueChange={setValues} />
Live Editor
<Autocomplete
  selectionMode="multiple"
  color="primary"
  placeholder="Search fruits..."
  defaultValue={['apple', 'banana']}
  options={[
    { value: 'apple', label: 'Apple' },
    { value: 'banana', label: 'Banana' },
    { value: 'cherry', label: 'Cherry' },
    { value: 'date', label: 'Date' },
  ]}
/>
Result
Loading...

Selection indicator

In the listbox, selected options are marked with a check icon at the very end of the option, after its endContent (selectionIndicator="check", the default), in both selection modes. Set selectionIndicator="none" to keep only the bolder label as the selected state.

The space for the icon is reserved on the other options so their content stays aligned; since the icon sits on the trailing edge, labels and startContent are never shifted.

Live Editor
<Autocomplete
  selectionMode="multiple"
  selectionIndicator="none"
  placeholder="Search fruits..."
  defaultValue={['apple']}
  options={[
    { value: 'apple', label: 'Apple' },
    { value: 'banana', label: 'Banana' },
    { value: 'cherry', label: 'Cherry' },
  ]}
/>
Result
Loading...

States

Loading

Set isLoading to show a spinner and disable the field.

Live Editor
<Autocomplete
  isLoading
  placeholder="Loading..."
  options={[{ value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }]}
/>
Result
Loading...

Disabled

Live Editor
<Autocomplete
  isDisabled
  placeholder="Disabled"
  options={[{ value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }]}
/>
Result
Loading...

Full width

Live Editor
<Autocomplete
  isFullWidth
  placeholder="Full width"
  options={[{ value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }]}
/>
Result
Loading...

Label & description

Live Editor
<Autocomplete
  label="Fruit"
  description="Pick your favorite fruit."
  placeholder="Search a fruit..."
  options={[{ value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }]}
/>
Result
Loading...

Use descriptionPlacement to render the description below the label instead of below the field:

Live Editor
<Autocomplete
  label="Fruit"
  description="Pick your favorite fruit."
  descriptionPlacement="label"
  placeholder="Search a fruit..."
  options={[{ value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }]}
/>
Result
Loading...

Required

Add isRequired to mark the field as required. An asterisk appears next to the label.

Live Editor
<Autocomplete
  label="Fruit"
  isRequired
  placeholder="Search a fruit..."
  options={[{ value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }]}
/>
Result
Loading...

Error

Pass an error string to show a message below the field. This is the externally controlled error channel: use it for errors the field cannot produce on its own (a server-side response, an async check, a cross-field rule). It always takes precedence over the field's own validation (isRequired, per-option validations), so a non-empty error is always the message shown.

Live Editor
<Autocomplete
  label="Fruit"
  error="Please select a fruit."
  placeholder="Search a fruit..."
  options={[{ value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }]}
/>
Result
Loading...

Options

Each entry in options accepts:

FieldTypeDescription
valuestringUnique value, passed to onValueChange
labelstringText shown in the input and the listbox
descriptionstringHelper text shown below the option label
isDisabledbooleanPrevents selecting this option
startContentReactNodeIcon (or any node) shown before the label
endContentReactNodeIcon (or any node) shown after the label
Live Editor
<Autocomplete
  placeholder="Search a fruit..."
  options={[
    { value: 'apple', label: 'Apple', description: 'Crisp and sweet', startContent: <Apple size={16} /> },
    { value: 'banana', label: 'Banana', description: 'Soft and creamy' },
  ]}
/>
Result
Loading...

Validation

Unlike text fields, validations runs against each option, not the selected value. It is an array of (option: AutocompleteOption) => string | null functions: an option for which a validator returns a message is disabled, and the message is shown as that option's description. If the currently selected value becomes invalid, it is automatically deselected.

The only value-level check is isRequired, which verifies that an option is selected.

Live Editor
<Autocomplete
  placeholder="Search a fruit..."
  validations={[(option) => (option.value === 'cherry' ? 'Out of season' : null)]}
  options={[
    { value: 'apple', label: 'Apple' },
    { value: 'banana', label: 'Banana' },
    { value: 'cherry', label: 'Cherry' },
  ]}
/>
Result
Loading...

Slots

Customize individual parts via classNames:

SlotDescription
baseRoot <div> wrapping the whole field
labelThe <label> element
inputWrapperThe bordered/tinted box wrapping the input
inputThe <input> element
chipsWrapper around the chips (multiple mode)
chipEach selected value chip
chipLabelThe label inside a chip
chipRemoveButtonThe remove control inside a chip
startContentWrapper around startContent
endContentWrapper around endContent
arrowWrapper around the arrow icon
spinnerThe loading spinner
listboxThe options <ul> container
optionEach <li> option
descriptionThe helper text element
errorThe error message element
Live Editor
<Autocomplete
  classNames={{ inputWrapper: 'shadow-md', listbox: 'shadow-lg' }}
  placeholder="Custom slots"
  options={[{ value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }]}
/>
Result
Loading...

These slots can also be styled globally for every Autocomplete in your app, see the global configuration guide.

Props

PropTypeDefaultDescription
optionsAutocompleteOption[]-The list of selectable options
selectionMode'single' | 'multiple''single'Whether one or several options can be selected
selectionIndicator'check' | 'none''check'Check icon at the end of selected options
valuestring (single) | string[] (multiple)-Controlled selected value
defaultValuestring (single) | string[] (multiple)-Initial value for uncontrolled usage
onValueChange(value: string) => void | (values: string[]) => void-Called when the selection changes
placeholderstring-Text shown when the field is empty
variant'bordered' | 'faded' | 'flat' | 'underlined''bordered'Visual style
color'default' | 'primary' | 'secondary' | 'success' | 'warning' | 'error''default'Focus color
size'sm' | 'md' | 'lg''md'Field size
radius'none' | 'sm' | 'md' | 'lg' | 'full''md'Corner radius
isDisabledbooleanfalseDisables the field
isLoadingbooleanfalseShows a spinner and disables the field
isRequiredbooleanfalseMarks the field as required
isRequiredMessagestring-Message shown when a required field has no selection
isFullWidthbooleanfalseMakes the field take the full available width
labelstring-Label rendered above the field
descriptionstring-Helper text
descriptionPlacement'label' | 'element''element'Render description below the label or below the field
errorstring-Externally controlled error, takes precedence over local validation
validationsArray<(option: AutocompleteOption) => string | null>-Per-option validators; invalid options are disabled
startContentReactNode-Content rendered before the input
endContentReactNode-Content rendered after the input
startContentPlacement'inside' | 'outside''inside'Where startContent is rendered
endContentPlacement'inside' | 'outside''inside'Where endContent is rendered
noResultsMessagestring'No results found'Message shown when no option matches the typed text
isInputClearedOnFocusbooleanfalseEmpties the input on focus so a new search can be typed; the committed value is kept
arrowReactNode-Custom node replacing the default chevron icon
arrowPlacement'start' | 'end''end'Where the arrow is rendered
isArrowHiddenbooleanfalseHides the arrow
classNamestring-Class applied to the root element
classNamesPartial<Record<AutocompleteSlots, string>>-Per-slot class overrides

Autocomplete forwards ref to the underlying <input> element.

Storybook

Open in Storybook → and see more combinations.