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..." />
}
<Autocomplete color="primary" placeholder="Search a fruit..." options={[ { value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }, { value: 'cherry', label: 'Cherry' }, ]} />
Variants
| Variant | Description |
|---|---|
bordered | Full border, transparent background (default) |
faded | Full border, tinted background |
flat | Tinted background, no border |
underlined | Bottom border only, no radius |
<Autocomplete variant="bordered" placeholder="Bordered" options={[{ value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }]} />
Colors
defaultprimarysecondarysuccesswarningerror
Variant affects colors. Have a look to Storybook to see how the component is rendering depending on the variant.
<Autocomplete color="primary" placeholder="Primary" options={[{ value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }]} />
Sizes
smmdlg
<Autocomplete size="sm" placeholder="Small" options={[{ value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }]} />
Radius
nonesmmdlgfull
<Autocomplete radius="sm" placeholder="Rounded" options={[{ value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }]} />
sm, md and lg map to --easyui-radius-* CSS variables, see the theming guide.
The
underlinedvariant always usesrounded-noneregardless of theradiusprop.
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.
<Autocomplete startContent={<Apple size={16} />} placeholder="Search a fruit..." options={[{ value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }]} />
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.
isArrowHiddenremoves it entirelyarrowPlacementmoves it to thestartorend(default) of the fieldarrowreplaces the default icon with any node
<Autocomplete arrowPlacement="start" placeholder="Arrow on the left" options={[{ value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }]} />
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.
<Autocomplete placeholder={'Try typing "an"...'} options={[ { value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }, { value: 'mango', label: 'Mango' }, { value: 'cherry', label: 'Cherry' }, ]} />
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
<Autocomplete label="Fruit" defaultValue="apple" isInputClearedOnFocus placeholder="Search a fruit..." options={[ { value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }, { value: 'mango', label: 'Mango' }, ]} />
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:
selectionMode | value / defaultValue | onValueChange |
|---|---|---|
'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} />
<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' }, ]} />
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.
<Autocomplete selectionMode="multiple" selectionIndicator="none" placeholder="Search fruits..." defaultValue={['apple']} options={[ { value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }, { value: 'cherry', label: 'Cherry' }, ]} />
States
Loading
Set isLoading to show a spinner and disable the field.
<Autocomplete isLoading placeholder="Loading..." options={[{ value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }]} />
Disabled
<Autocomplete isDisabled placeholder="Disabled" options={[{ value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }]} />
Full width
<Autocomplete isFullWidth placeholder="Full width" options={[{ value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }]} />
Label & description
<Autocomplete label="Fruit" description="Pick your favorite fruit." placeholder="Search a fruit..." options={[{ value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }]} />
Use descriptionPlacement to render the description below the label instead of below the field:
<Autocomplete label="Fruit" description="Pick your favorite fruit." descriptionPlacement="label" placeholder="Search a fruit..." options={[{ value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }]} />
Required
Add isRequired to mark the field as required. An asterisk appears next to the label.
<Autocomplete label="Fruit" isRequired placeholder="Search a fruit..." options={[{ value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }]} />
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.
<Autocomplete label="Fruit" error="Please select a fruit." placeholder="Search a fruit..." options={[{ value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }]} />
Options
Each entry in options accepts:
| Field | Type | Description |
|---|---|---|
value | string | Unique value, passed to onValueChange |
label | string | Text shown in the input and the listbox |
description | string | Helper text shown below the option label |
isDisabled | boolean | Prevents selecting this option |
startContent | ReactNode | Icon (or any node) shown before the label |
endContent | ReactNode | Icon (or any node) shown after the label |
<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' }, ]} />
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.
<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' }, ]} />
Slots
Customize individual parts via classNames:
| Slot | Description |
|---|---|
base | Root <div> wrapping the whole field |
label | The <label> element |
inputWrapper | The bordered/tinted box wrapping the input |
input | The <input> element |
chips | Wrapper around the chips (multiple mode) |
chip | Each selected value chip |
chipLabel | The label inside a chip |
chipRemoveButton | The remove control inside a chip |
startContent | Wrapper around startContent |
endContent | Wrapper around endContent |
arrow | Wrapper around the arrow icon |
spinner | The loading spinner |
listbox | The options <ul> container |
option | Each <li> option |
description | The helper text element |
error | The error message element |
<Autocomplete classNames={{ inputWrapper: 'shadow-md', listbox: 'shadow-lg' }} placeholder="Custom slots" options={[{ value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }]} />
These slots can also be styled globally for every Autocomplete in your app, see the global configuration guide.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
options | AutocompleteOption[] | - | 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 |
value | string (single) | string[] (multiple) | - | Controlled selected value |
defaultValue | string (single) | string[] (multiple) | - | Initial value for uncontrolled usage |
onValueChange | (value: string) => void | (values: string[]) => void | - | Called when the selection changes |
placeholder | string | - | 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 |
isDisabled | boolean | false | Disables the field |
isLoading | boolean | false | Shows a spinner and disables the field |
isRequired | boolean | false | Marks the field as required |
isRequiredMessage | string | - | Message shown when a required field has no selection |
isFullWidth | boolean | false | Makes the field take the full available width |
label | string | - | Label rendered above the field |
description | string | - | Helper text |
descriptionPlacement | 'label' | 'element' | 'element' | Render description below the label or below the field |
error | string | - | Externally controlled error, takes precedence over local validation |
validations | Array<(option: AutocompleteOption) => string | null> | - | Per-option validators; invalid options are disabled |
startContent | ReactNode | - | Content rendered before the input |
endContent | ReactNode | - | Content rendered after the input |
startContentPlacement | 'inside' | 'outside' | 'inside' | Where startContent is rendered |
endContentPlacement | 'inside' | 'outside' | 'inside' | Where endContent is rendered |
noResultsMessage | string | 'No results found' | Message shown when no option matches the typed text |
isInputClearedOnFocus | boolean | false | Empties the input on focus so a new search can be typed; the committed value is kept |
arrow | ReactNode | - | Custom node replacing the default chevron icon |
arrowPlacement | 'start' | 'end' | 'end' | Where the arrow is rendered |
isArrowHidden | boolean | false | Hides the arrow |
className | string | - | Class applied to the root element |
classNames | Partial<Record<AutocompleteSlots, string>> | - | Per-slot class overrides |
Autocomplete forwards ref to the underlying <input> element.
Storybook
Open in Storybook → and see more combinations.