27.5k

Autocomplete

Migration guide for Autocomplete (renamed to ComboBox) from HeroUI v2 to v3

Refer to the v3 ComboBox documentation for complete API reference, styling guide, and advanced examples. This guide only focuses on migrating from HeroUI v2.

Overview

The Autocomplete component has been renamed to ComboBox in HeroUI v3 and redesigned with a compound component pattern, requiring explicit structure with ComboBox.InputGroup, ComboBox.Trigger, ComboBox.Popover, and ListBox components.

Structure Changes

v2: Single Component with Internal Structure

In v2, Autocomplete was a single component that wrapped Input internally:

import { Autocomplete, AutocompleteItem } from "@heroui/react";

<Autocomplete label="Select an animal">
  <AutocompleteItem key="cat">Cat</AutocompleteItem>
  <AutocompleteItem key="dog">Dog</AutocompleteItem>
</Autocomplete>

v3: Compound Component Pattern

In v3, ComboBox uses compound components and requires explicit Input:

import { ComboBox, Input, Label, ListBox } from "@heroui/react";

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

Key Changes

1. Component Rename

v2: Autocomplete
v3: ComboBox

2. Component Structure

v2: Single component with internal Input
v3: Compound components (ComboBox.InputGroup, ComboBox.Trigger, ComboBox.Popover)

3. Item Components

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

4. Prop Changes

v2 Propv3 PropNotes
key (on AutocompleteItem)id (on ListBox.Item)Changed prop name
-textValue (on ListBox.Item)Now required for accessibility
labelLabel componentSeparate component
descriptionDescription componentSeparate component
placeholderplaceholder (on Input)Moved to Input component
variant-Removed (use Tailwind CSS)
color-Removed (use Tailwind CSS)
size-Removed (use Tailwind CSS)
radius-Removed (use Tailwind CSS)
labelPlacement-Removed (use Label component positioning)
startContent-Removed (add to Input directly)
endContent-Removed (add to Input directly)
selectorIcon-Removed (customize ComboBox.Trigger)
clearIcon-Removed (not built-in)
isClearable-Removed (implement manually)
showScrollIndicators-Removed
classNames-Use className props
selectedKeyselectedKeyStill exists
onSelectionChangeonSelectionChangeStill exists
inputValueinputValueStill exists
onInputChangeonInputChangeStill exists
allowsCustomValueallowsCustomValueStill exists
allowsEmptyCollectionallowsEmptyCollectionStill exists
defaultFilterdefaultFilterStill exists
disabledKeysdisabledKeysStill exists
isDisabledisDisabledStill exists
isRequiredisRequiredStill exists
isInvalidisInvalidStill exists
namenameStill exists

5. Removed Props

The following props are no longer available in v3:

  • variant, color, size, radius - Use Tailwind CSS classes
  • labelPlacement - Use Label component positioning
  • startContent, endContent - Add directly to Input component
  • selectorIcon, clearIcon - Customize ComboBox.Trigger or implement manually
  • isClearable - Implement clear functionality manually
  • showScrollIndicators - Not supported
  • classNames - Use className props on individual components
  • popoverProps, listboxProps, inputProps - Configure components directly
  • scrollShadowProps - Not supported
  • selectorButtonProps, clearButtonProps - Not applicable
  • disableSelectorIconRotation - Not applicable
  • menuTrigger - Handled automatically
  • isReadOnly - Use Input readOnly prop
  • fullWidth - Use Tailwind CSS classes
  • isVirtualized, maxListboxHeight, itemHeight - Not supported
  • scrollRef - Not supported
  • onClose, onClear - Use different event handlers
  • validationBehavior, validate - Use Form component

Migration Examples

Basic Usage

import { Autocomplete, AutocompleteItem } from "@heroui/react";

export default function App() {
  return (
    <Autocomplete className="max-w-xs" label="Select an animal">
      <AutocompleteItem key="cat">Cat</AutocompleteItem>
      <AutocompleteItem key="dog">Dog</AutocompleteItem>
      <AutocompleteItem key="bird">Bird</AutocompleteItem>
    </Autocomplete>
  );
}
import { ComboBox, Input, Label, ListBox } from "@heroui/react";

export default function App() {
  return (
    <ComboBox className="max-w-xs">
      <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.Item id="bird" textValue="Bird">
            Bird
            <ListBox.ItemIndicator />
          </ListBox.Item>
        </ListBox>
      </ComboBox.Popover>
    </ComboBox>
  );
}

With Description

<Autocomplete
  label="Select an animal"
  description="Choose your favorite animal"
>
  <AutocompleteItem key="cat">Cat</AutocompleteItem>
</Autocomplete>
import { Description } from "@heroui/react";

<ComboBox>
  <Label>Select an animal</Label>
  <ComboBox.InputGroup>
    <Input placeholder="Search animals..." />
    <ComboBox.Trigger />
  </ComboBox.InputGroup>
  <Description>Choose your favorite animal</Description>
  <ComboBox.Popover>
    <ListBox>
      <ListBox.Item id="cat" textValue="Cat">
        Cat
        <ListBox.ItemIndicator />
      </ListBox.Item>
    </ListBox>
  </ComboBox.Popover>
</ComboBox>

Controlled Selection

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>

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

const animals = [
  {label: "Cat", key: "cat"},
  {label: "Dog", key: "dog"}
];

<Autocomplete defaultItems={animals} label="Animal">
  {(item) => (
    <AutocompleteItem key={item.key}>
      {item.label}
    </AutocompleteItem>
  )}
</Autocomplete>
const animals = [
  {id: "cat", name: "Cat"},
  {id: "dog", name: "Dog"}
];

<ComboBox items={animals}>
  <Label>Animal</Label>
  <ComboBox.InputGroup>
    <Input placeholder="Search animals..." />
    <ComboBox.Trigger />
  </ComboBox.InputGroup>
  <ComboBox.Popover>
    <ListBox>
      {(item) => (
        <ListBox.Item id={item.id} textValue={item.name}>
          {item.name}
          <ListBox.ItemIndicator />
        </ListBox.Item>
      )}
    </ListBox>
  </ComboBox.Popover>
</ComboBox>

With Custom Filtering

<Autocomplete
  label="Animal"
  defaultFilter={(text, inputValue) => {
    if (!inputValue) return true;
    return text.toLowerCase().includes(inputValue.toLowerCase());
  }}
>
  <AutocompleteItem key="cat">Cat</AutocompleteItem>
</Autocomplete>
<ComboBox
  defaultFilter={(text, inputValue) => {
    if (!inputValue) return true;
    return text.toLowerCase().includes(inputValue.toLowerCase());
  }}
>
  <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>
</ComboBox>

With Disabled Items

<Autocomplete
  label="Animal"
  disabledKeys={["dog"]}
>
  <AutocompleteItem key="cat">Cat</AutocompleteItem>
  <AutocompleteItem key="dog">Dog</AutocompleteItem>
</Autocomplete>
<ComboBox disabledKeys={["dog"]}>
  <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>

Required Field

<Autocomplete
  label="Animal"
  isRequired
>
  <AutocompleteItem key="cat">Cat</AutocompleteItem>
</Autocomplete>
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>

With Error Message

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

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

Custom Styling

<Autocomplete
  label="Animal"
  variant="bordered"
  color="primary"
  size="lg"
  classNames={{
    base: "custom-base",
    listbox: "custom-listbox"
  }}
>
  <AutocompleteItem key="cat">Cat</AutocompleteItem>
</Autocomplete>
<ComboBox className="custom-combobox">
  <Label>Animal</Label>
  <ComboBox.InputGroup className="custom-input-group">
    <Input placeholder="Search animals..." className="custom-input" />
    <ComboBox.Trigger className="custom-trigger" />
  </ComboBox.InputGroup>
  <ComboBox.Popover className="custom-popover">
    <ListBox className="custom-listbox">
      <ListBox.Item id="cat" textValue="Cat" className="custom-item">
        Cat
        <ListBox.ItemIndicator />
      </ListBox.Item>
    </ListBox>
  </ComboBox.Popover>
</ComboBox>

Component Anatomy

The v3 ComboBox follows this structure:

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)

Important Notes

Component Rename

  • v2: Autocompletev3: ComboBox
  • Update all imports and component references

Item Components

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

Item Props

  • v2: Used key prop on AutocompleteItem
  • v3: Use id prop on ListBox.Item (required) and textValue prop (required)

Input Component

  • v2: Input was internal to Autocomplete
  • v3: Must explicitly include Input component inside ComboBox.InputGroup

Label and Description

  • v2: Props on Autocomplete component
  • v3: Separate Label and Description components

Selection Indicators

  • v2: Automatic selection indicators
  • v3: Must explicitly include ListBox.ItemIndicator component in items

Clear Button

  • v2: Built-in clear button with isClearable prop
  • v3: Not built-in - implement manually if needed

Styling

  • v2: Many styling props (variant, color, size, radius)
  • v3: Use Tailwind CSS classes directly

Breaking Changes Summary

  1. Component Rename: AutocompleteComboBox
  2. Component Structure: Single component → Compound components
  3. Item Components: AutocompleteItemListBox.Item
  4. Item Props: keyid + textValue (required)
  5. Input: Internal → Explicit Input component required
  6. Label/Description: Props → Separate components
  7. Styling Props Removed: variant, color, size, radius - use Tailwind CSS
  8. Clear Button: Built-in → Manual implementation required
  9. ClassNames Removed: Use className props on individual components
  10. Many Props Removed: See prop changes table above

Tips for Migration

  1. Rename component: Change Autocomplete to ComboBox
  2. Update imports: Import ComboBox, Input, Label, ListBox from @heroui/react
  3. Restructure: Use compound component pattern with ComboBox.InputGroup, ComboBox.Trigger, ComboBox.Popover
  4. Add Input: Include explicit Input component
  5. Update items: Change AutocompleteItem to ListBox.Item
  6. Add textValue: Add textValue prop to all items (required)
  7. Update keys: Change key prop to id prop
  8. Add indicators: Include ListBox.ItemIndicator for selection indicators
  9. Update labels: Use Label component instead of label prop
  10. Update descriptions: Use Description component instead of description prop
  11. Update styling: Replace styling props with Tailwind CSS classes
  12. Handle clear: Implement clear button manually if needed

Need Help?

For v3 ComboBox features and API:

For community support: