apps/docs/content/docs/en/react/migration/(components)/autocomplete.mdx
V3 provides two components that replace the v2 Autocomplete:
| Component | Best For | Input Style | Built on |
|---|---|---|---|
| Autocomplete | Select-style picker with a search/filter field inside the popover | Button trigger that shows selected value; search input inside popover | React Aria Select + Autocomplete |
| ComboBox | Text input that filters a dropdown list as you type | Inline text input with dropdown trigger | React Aria ComboBox |
Use Autocomplete when the user picks from a predefined list and the search field should appear inside the dropdown (similar to a searchable select).
Use ComboBox when the user types directly into a visible input field to filter or search options (similar to v2 behavior).
In v2, Autocomplete was a single component that wrapped Input internally:
import { Autocomplete, AutocompleteItem } from "@heroui/react";
export default function App() {
return (
<Autocomplete label="Select an animal">
<AutocompleteItem key="cat">Cat</AutocompleteItem>
<AutocompleteItem key="dog">Dog</AutocompleteItem>
</Autocomplete>
);
}
ComboBox provides the most similar experience to the v2 Autocomplete, with an inline text input that filters options:
import { ComboBox, Input, Label, ListBox } from "@heroui/react";
export default function App() {
return (
<ComboBox>
<Label>Select an animal</Label>
<ComboBox.InputGroup>
<Input placeholder="Search animals..." />
<ComboBox.Trigger />
</ComboBox.InputGroup>
<ComboBox.Popover>
<ListBox>
<ListBox.Item id="cat" textValue="Cat">
Cat
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="dog" textValue="Dog">
Dog
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</ComboBox.Popover>
</ComboBox>
);
}
Autocomplete provides a select-style trigger with a search/filter field inside the popover:
import { Autocomplete, Label, SearchField, ListBox, useFilter } from "@heroui/react";
export default function App() {
const { contains } = useFilter({ sensitivity: "base" });
return (
<Autocomplete>
<Label>Select an animal</Label>
<Autocomplete.Trigger>
<Autocomplete.Value />
<Autocomplete.ClearButton />
<Autocomplete.Indicator />
</Autocomplete.Trigger>
<Autocomplete.Popover>
<Autocomplete.Filter filter={contains}>
<SearchField>
<SearchField.Group>
<SearchField.SearchIcon />
<SearchField.Input placeholder="Search..." />
</SearchField.Group>
</SearchField>
<ListBox>
<ListBox.Item id="cat" textValue="Cat">
Cat
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="dog" textValue="Dog">
Dog
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</Autocomplete.Filter>
</Autocomplete.Popover>
</Autocomplete>
);
}
v2: Autocomplete (single component)
v3: ComboBox (inline input + dropdown) or Autocomplete (select-style with search in popover)
v2: Single component with internal Input
v3 ComboBox: Compound components (ComboBox.InputGroup, ComboBox.Trigger, ComboBox.Popover)
v3 Autocomplete: Compound components (Autocomplete.Trigger, Autocomplete.Value, Autocomplete.ClearButton, Autocomplete.Indicator, Autocomplete.Popover, Autocomplete.Filter)
v2: AutocompleteItem, AutocompleteSection
v3: ListBox.Item, ListBox.Section (from ListBox component)
v2: React's key was used for both list reconciliation and item identity (selection).
v3: Use id and textValue on ListBox.Item (state and accessibility); keep React's key on items in lists.
The v3 Autocomplete introduces several subcomponents not present in v2:
| Subcomponent | Purpose |
|---|---|
Autocomplete.Trigger | The button group that opens the popover |
Autocomplete.Value | Displays the currently selected value or placeholder |
Autocomplete.ClearButton | Clears the current selection |
Autocomplete.Indicator | Dropdown chevron icon (rotates when open) |
Autocomplete.Popover | The dropdown popover container |
Autocomplete.Filter | Wraps SearchField and ListBox to enable filtering; accepts a filter function, inputValue, and onInputChange |
The v3 Autocomplete uses SearchField inside Autocomplete.Filter for the search input within the popover:
<Autocomplete.Filter filter={contains}>
<SearchField>
<SearchField.Group>
<SearchField.SearchIcon />
<SearchField.Input placeholder="Search..." />
</SearchField.Group>
</SearchField>
<ListBox>...</ListBox>
</Autocomplete.Filter>
SearchField has its own subcomponents: SearchField.Group, SearchField.Input, SearchField.SearchIcon, and SearchField.ClearButton.
The v3 Autocomplete uses the useFilter hook (from React Aria, re-exported by @heroui/react) to provide locale-aware filtering functions:
import { useFilter } from "@heroui/react";
const { contains } = useFilter({ sensitivity: "base" });
// Pass to Autocomplete.Filter
<Autocomplete.Filter filter={contains}>
...
</Autocomplete.Filter>
The hook returns contains, startsWith, and endsWith functions. The sensitivity option controls locale-aware matching ("base", "accent", "case", "variant").
| v2 Prop | v3 Location | Notes |
|---|---|---|
| - | id (on ListBox.Item) | Item identifier for state |
| - | textValue (on ListBox.Item) | For accessibility (type-ahead) |
label | Label | Use Label component |
description | Description | Use Description component |
placeholder | Input | placeholder on Input |
selectedKey, onSelectionChange, inputValue, onInputChange | ComboBox | Same |
allowsCustomValue, allowsEmptyCollection, defaultFilter | ComboBox | Same |
disabledKeys, isDisabled, isRequired, isInvalid, name | ComboBox | Same |
menuTrigger | ComboBox | "focus" (default), "input", or "manual" |
variant, color, size, radius | - | Removed (use Tailwind CSS) |
labelPlacement | - | Removed (use Label positioning) |
startContent, endContent | - | Add to Input or InputGroup |
selectorIcon, clearIcon | - | Customize ComboBox.Trigger or implement manually |
isClearable | - | Implement manually |
showScrollIndicators | - | Removed |
classNames | - | Use className on parts |
popoverProps, listboxProps, inputProps | - | Configure components directly |
scrollShadowProps, scrollRef | - | Removed |
selectorButtonProps, clearButtonProps, disableSelectorIconRotation | - | Removed |
isReadOnly | ComboBox | isReadOnly prop on ComboBox |
fullWidth | ComboBox | fullWidth prop on ComboBox |
isVirtualized, maxListboxHeight, itemHeight | - | Removed |
onClose, onClear | - | Use other event handlers |
validationBehavior, validate | ComboBox | Props on ComboBox |
| v2 Prop | v3 Location | Notes |
|---|---|---|
| - | id (on ListBox.Item) | Item identifier for state |
| - | textValue (on ListBox.Item) | For accessibility (type-ahead) |
label | Label | Use Label component |
description | Description | Use Description component |
placeholder | Autocomplete | placeholder prop on root |
selectedKey / onSelectionChange | value / onChange | Renamed props on Autocomplete |
selectionMode | Autocomplete | "single" (default) or "multiple" |
inputValue, onInputChange | Autocomplete.Filter | inputValue and onInputChange on Filter |
disabledKeys, isDisabled, isRequired, isInvalid, name | Autocomplete | Same |
variant, color, size, radius | - | Removed (use Tailwind CSS); variant supports "primary" / "secondary" |
isClearable | Autocomplete.ClearButton | Built-in subcomponent |
selectorIcon | Autocomplete.Indicator | Pass custom icon as children |
onClear | Autocomplete | onClear prop on root |
fullWidth | Autocomplete | fullWidth prop on root |
classNames | - | Use className on parts |
popoverProps, listboxProps, inputProps | - | Configure components directly |
<Tabs items={["v2", "v3"]}> <Tab value="v2"> ```tsx import { useState } from "react";
const [selectedKey, setSelectedKey] = useState("cat");
<Autocomplete
selectedKey={selectedKey}
onSelectionChange={setSelectedKey}
label="Animal"
>
<AutocompleteItem key="cat">Cat</AutocompleteItem>
<AutocompleteItem key="dog">Dog</AutocompleteItem>
</Autocomplete>
```
const [selectedKey, setSelectedKey] = useState<Key | null>("cat");
<ComboBox
selectedKey={selectedKey}
onSelectionChange={setSelectedKey}
>
<Label>Animal</Label>
<ComboBox.InputGroup>
<Input placeholder="Search animals..." />
<ComboBox.Trigger />
</ComboBox.InputGroup>
<ComboBox.Popover>
<ListBox>
<ListBox.Item id="cat" textValue="Cat">
Cat
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="dog" textValue="Dog">
Dog
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</ComboBox.Popover>
</ComboBox>
```
<Tabs items={["v2", "v3"]}> <Tab value="v2"> ```tsx import { useState } from "react";
const [selectedKey, setSelectedKey] = useState("cat");
<Autocomplete
selectedKey={selectedKey}
onSelectionChange={setSelectedKey}
label="Animal"
>
<AutocompleteItem key="cat">Cat</AutocompleteItem>
<AutocompleteItem key="dog">Dog</AutocompleteItem>
</Autocomplete>
```
const [value, setValue] = useState<Key | Key[] | null>("cat");
const { contains } = useFilter({ sensitivity: "base" });
<Autocomplete value={value} onChange={setValue}>
<Label>Animal</Label>
<Autocomplete.Trigger>
<Autocomplete.Value />
<Autocomplete.ClearButton />
<Autocomplete.Indicator />
</Autocomplete.Trigger>
<Autocomplete.Popover>
<Autocomplete.Filter filter={contains}>
<SearchField>
<SearchField.Group>
<SearchField.SearchIcon />
<SearchField.Input placeholder="Search..." />
</SearchField.Group>
</SearchField>
<ListBox>
<ListBox.Item id="cat" textValue="Cat">
<Label>Cat</Label>
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="dog" textValue="Dog">
<Label>Dog</Label>
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</Autocomplete.Filter>
</Autocomplete.Popover>
</Autocomplete>
```
<Tabs items={["v2", "v3"]}>
<Tab value="v2">
tsx <Autocomplete label="Country"> <AutocompleteSection title="North America"> <AutocompleteItem key="usa">United States</AutocompleteItem> <AutocompleteItem key="canada">Canada</AutocompleteItem> </AutocompleteSection> <AutocompleteSection title="Europe"> <AutocompleteItem key="uk">United Kingdom</AutocompleteItem> </AutocompleteSection> </Autocomplete>
</Tab>
<Tab value="v3">
```tsx
import { Header, Separator } from "@heroui/react";
<ComboBox>
<Label>Country</Label>
<ComboBox.InputGroup>
<Input placeholder="Search countries..." />
<ComboBox.Trigger />
</ComboBox.InputGroup>
<ComboBox.Popover>
<ListBox>
<ListBox.Section>
<Header>North America</Header>
<ListBox.Item id="usa" textValue="United States">
United States
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="canada" textValue="Canada">
Canada
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox.Section>
<Separator />
<ListBox.Section>
<Header>Europe</Header>
<ListBox.Item id="uk" textValue="United Kingdom">
United Kingdom
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox.Section>
</ListBox>
</ComboBox.Popover>
</ComboBox>
```
<Tabs items={["v2", "v3"]}>
<Tab value="v2">
tsx <Autocomplete label="Animal"> <AutocompleteItem key="cat">Cat</AutocompleteItem> <AutocompleteItem key="dog">Dog</AutocompleteItem> </Autocomplete>
</Tab>
<Tab value="v3">
```tsx
import { Autocomplete, Label, SearchField, ListBox, useFilter } from "@heroui/react";
function FilterExample() {
const { contains } = useFilter({ sensitivity: "base" });
return (
<Autocomplete>
<Label>Animal</Label>
<Autocomplete.Trigger>
<Autocomplete.Value />
<Autocomplete.ClearButton />
<Autocomplete.Indicator />
</Autocomplete.Trigger>
<Autocomplete.Popover>
<Autocomplete.Filter filter={contains}>
<SearchField>
<SearchField.Group>
<SearchField.SearchIcon />
<SearchField.Input placeholder="Search..." />
</SearchField.Group>
</SearchField>
<ListBox>
<ListBox.Item id="cat" textValue="Cat">
<Label>Cat</Label>
<ListBox.ItemIndicator />
</ListBox.Item>
<ListBox.Item id="dog" textValue="Dog">
<Label>Dog</Label>
<ListBox.ItemIndicator />
</ListBox.Item>
</ListBox>
</Autocomplete.Filter>
</Autocomplete.Popover>
</Autocomplete>
);
}
```
<Tabs items={["v2", "v3"]}>
<Tab value="v2">
tsx <Autocomplete label="Animal" isRequired> <AutocompleteItem key="cat">Cat</AutocompleteItem> </Autocomplete> <Autocomplete label="Animal" isInvalid errorMessage="Please select an animal" > <AutocompleteItem key="cat">Cat</AutocompleteItem> </Autocomplete>
</Tab>
<Tab value="v3">
tsx import { FieldError, Form } from "@heroui/react"; <Form> <ComboBox isRequired name="animal"> <Label>Animal</Label> <ComboBox.InputGroup> <Input placeholder="Search animals..." /> <ComboBox.Trigger /> </ComboBox.InputGroup> <ComboBox.Popover> <ListBox> <ListBox.Item id="cat" textValue="Cat"> Cat <ListBox.ItemIndicator /> </ListBox.Item> </ListBox> </ComboBox.Popover> <FieldError /> </ComboBox> </Form> <ComboBox isInvalid> <Label>Animal</Label> <ComboBox.InputGroup> <Input placeholder="Search animals..." /> <ComboBox.Trigger /> </ComboBox.InputGroup> <ComboBox.Popover> <ListBox> <ListBox.Item id="cat" textValue="Cat"> Cat <ListBox.ItemIndicator /> </ListBox.Item> </ListBox> </ComboBox.Popover> <FieldError>Please select an animal</FieldError> </ComboBox>
</Tab>
</Tabs>
ComboBox (Root)
├── Label
├── ComboBox.InputGroup
│ ├── Input
│ └── ComboBox.Trigger
├── Description (optional)
├── ComboBox.Popover
│ └── ListBox
│ ├── ListBox.Item
│ │ ├── [Content]
│ │ └── ListBox.ItemIndicator (optional)
│ └── ListBox.Section (optional)
│ ├── Header
│ └── ListBox.Item
└── FieldError (optional)
Autocomplete (Root)
├── Label
├── Autocomplete.Trigger
│ ├── Autocomplete.Value
│ ├── Autocomplete.ClearButton
│ └── Autocomplete.Indicator
├── Description (optional)
├── Autocomplete.Popover
│ └── Autocomplete.Filter
│ ├── SearchField
│ │ └── SearchField.Group
│ │ ├── SearchField.SearchIcon
│ │ └── SearchField.Input
│ └── ListBox
│ ├── ListBox.Item
│ │ ├── [Content]
│ │ └── ListBox.ItemIndicator (optional)
│ └── ListBox.Section (optional)
│ ├── Header
│ └── ListBox.Item
└── FieldError (optional)
Autocomplete can be migrated to v3 ComboBox (inline input) or v3 Autocomplete (searchable select with filter in popover)AutocompleteItem becomes ListBox.Item; AutocompleteSection becomes ListBox.Sectionid and textValue on items; keep React's key for list reconciliationInput; v3 Autocomplete uses SearchField inside Autocomplete.FilterLabel and Description componentsAutocomplete.Filter with useFilter hook for locale-aware filtering; v3 ComboBox uses defaultFilter propAutocomplete.ClearButton; v3 ComboBox requires manual implementationAutocomplete.Value for displaying the selected value with render prop supportcolor, size, radius removed (use Tailwind CSS); variant now supports "primary" / "secondary"className props on individual subcomponentscontains, startsWith, endsWith for locale-aware text matching