Pro--% off in--d : --h : --m : --s
HeroUI

Autocomplete

Migration guide for Autocomplete from HeroUI v2 to v3

In v3, the v2 Autocomplete can be migrated to either the v3 Autocomplete or the v3 ComboBox, depending on your use case. See the section below on choosing the right component. This guide focuses on migrating from HeroUI v2.

Choosing Between Autocomplete and ComboBox

V3 provides two components that replace the v2 Autocomplete:

ComponentBest ForInput StyleBuilt on
AutocompleteSelect-style picker with a search/filter field inside the popoverButton trigger that shows selected value; search input inside popoverReact Aria Select + Autocomplete
ComboBoxText input that filters a dropdown list as you typeInline text input with dropdown triggerReact 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).

Structure Changes

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

Migrating to v3 ComboBox (closest to v2 behavior)

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

Migrating to v3 Autocomplete (searchable select)

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

Key Changes

1. Two Replacement Components

v2: Autocomplete (single component) v3: ComboBox (inline input + dropdown) or Autocomplete (select-style with search in popover)

2. Component Structure

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)

3. Item Components

v2: AutocompleteItem, AutocompleteSection v3: ListBox.Item, ListBox.Section (from ListBox component)

4. Item Identification

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.

5. Autocomplete-Specific Subcomponents (v3)

The v3 Autocomplete introduces several subcomponents not present in v2:

SubcomponentPurpose
Autocomplete.TriggerThe button group that opens the popover
Autocomplete.ValueDisplays the currently selected value or placeholder
Autocomplete.ClearButtonClears the current selection
Autocomplete.IndicatorDropdown chevron icon (rotates when open)
Autocomplete.PopoverThe dropdown popover container
Autocomplete.FilterWraps SearchField and ListBox to enable filtering; accepts a filter function, inputValue, and onInputChange

6. SearchField Integration

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.

7. useFilter Hook

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").

8. Prop Changes (ComboBox)

v2 Propv3 LocationNotes
-id (on ListBox.Item)Item identifier for state
-textValue (on ListBox.Item)For accessibility (type-ahead)
labelLabelUse Label component
descriptionDescriptionUse Description component
placeholderInputplaceholder on Input
selectedKey, onSelectionChange, inputValue, onInputChangeComboBoxSame
allowsCustomValue, allowsEmptyCollection, defaultFilterComboBoxSame
disabledKeys, isDisabled, isRequired, isInvalid, nameComboBoxSame
menuTriggerComboBox"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
isReadOnlyComboBoxisReadOnly prop on ComboBox
fullWidthComboBoxfullWidth prop on ComboBox
isVirtualized, maxListboxHeight, itemHeight-Removed
onClose, onClear-Use other event handlers
validationBehavior, validateComboBoxProps on ComboBox

9. Prop Changes (Autocomplete)

v2 Propv3 LocationNotes
-id (on ListBox.Item)Item identifier for state
-textValue (on ListBox.Item)For accessibility (type-ahead)
labelLabelUse Label component
descriptionDescriptionUse Description component
placeholderAutocompleteplaceholder prop on root
selectedKey / onSelectionChangevalue / onChangeRenamed props on Autocomplete
selectionModeAutocomplete"single" (default) or "multiple"
inputValue, onInputChangeAutocomplete.FilterinputValue and onInputChange on Filter
disabledKeys, isDisabled, isRequired, isInvalid, nameAutocompleteSame
variant, color, size, radius-Removed (use Tailwind CSS); variant supports "primary" / "secondary"
isClearableAutocomplete.ClearButtonBuilt-in subcomponent
selectorIconAutocomplete.IndicatorPass custom icon as children
onClearAutocompleteonClear prop on root
fullWidthAutocompletefullWidth prop on root
classNames-Use className on parts
popoverProps, listboxProps, inputProps-Configure components directly

Migration Examples

Controlled Selection (ComboBox)

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>
import { useState } from "react";
import type { Key } from "@heroui/react";

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>

Controlled Selection (Autocomplete)

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>
import { useState } from "react";
import type { Key } from "@heroui/react";
import { Autocomplete, Label, SearchField, ListBox, useFilter } from "@heroui/react";

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>

With Sections

<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>
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>

With useFilter (Autocomplete)

{/* v2 handled filtering internally */}
<Autocomplete label="Animal">
  <AutocompleteItem key="cat">Cat</AutocompleteItem>
  <AutocompleteItem key="dog">Dog</AutocompleteItem>
</Autocomplete>
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>
  );
}

Form Validation

{/* Required field */}
<Autocomplete label="Animal" isRequired>
  <AutocompleteItem key="cat">Cat</AutocompleteItem>
</Autocomplete>

{/* With error message */}
<Autocomplete
  label="Animal"
  isInvalid
  errorMessage="Please select an animal"
>
  <AutocompleteItem key="cat">Cat</AutocompleteItem>
</Autocomplete>
import { FieldError, Form } from "@heroui/react";

{/* Required field */}
<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>

{/* With error message */}
<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>

Component Anatomy

ComboBox Anatomy

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 Anatomy

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)

Summary

  1. Two Replacement Components: v2 Autocomplete can be migrated to v3 ComboBox (inline input) or v3 Autocomplete (searchable select with filter in popover)
  2. Component Structure: Single component with props becomes compound components with explicit subcomponents
  3. Item Components: AutocompleteItem becomes ListBox.Item; AutocompleteSection becomes ListBox.Section
  4. Item Identity: Use id and textValue on items; keep React's key for list reconciliation
  5. Input: v3 ComboBox requires explicit Input; v3 Autocomplete uses SearchField inside Autocomplete.Filter
  6. Label/Description: Props become separate Label and Description components
  7. Filtering: v3 Autocomplete uses Autocomplete.Filter with useFilter hook for locale-aware filtering; v3 ComboBox uses defaultFilter prop
  8. Clear Button: v3 Autocomplete has built-in Autocomplete.ClearButton; v3 ComboBox requires manual implementation
  9. Selection Value Display: v3 Autocomplete has Autocomplete.Value for displaying the selected value with render prop support
  10. Styling Props Removed: color, size, radius removed (use Tailwind CSS); variant now supports "primary" / "secondary"
  11. ClassNames Object Removed: Use className props on individual subcomponents
  12. useFilter Hook: New in v3, provides contains, startsWith, endsWith for locale-aware text matching

On this page