Skip to main content
The Autocomplete component provides an input field that shows suggestions as the user types, perfect for search boxes and assisted input.

Installation

npx shadcn@latest add @eo-n/autocomplete

Usage

import {
  Autocomplete,
  AutocompleteContent,
  AutocompleteEmpty,
  AutocompleteInput,
  AutocompleteItem,
  AutocompleteList,
} from "@/components/ui/autocomplete";

interface Fruit {
  id: string;
  value: string;
}

const fruits: Fruit[] = [
  { id: "a", value: "Apple" },
  { id: "b", value: "Banana" },
  { id: "c", value: "Cherry" },
  { id: "d", value: "Durian" },
];

export default function Example() {
  return (
    <Autocomplete items={fruits}>
      <AutocompleteInput placeholder="Search fruits..." />
      <AutocompleteContent>
        <AutocompleteEmpty>No fruits found.</AutocompleteEmpty>
        <AutocompleteList>
          {(fruit: Fruit) => (
            <AutocompleteItem key={fruit.id} value={fruit}>
              {fruit.value}
            </AutocompleteItem>
          )}
        </AutocompleteList>
      </AutocompleteContent>
    </Autocomplete>
  );
}

Examples

Default

<Autocomplete items={fruits}>
  <AutocompleteInput placeholder="Type to search..." />
  <AutocompleteContent>
    <AutocompleteEmpty>No results found.</AutocompleteEmpty>
    <AutocompleteList>
      {(fruit: Fruit) => (
        <AutocompleteItem key={fruit.id} value={fruit}>
          {fruit.value}
        </AutocompleteItem>
      )}
    </AutocompleteList>
  </AutocompleteContent>
</Autocomplete>

Disabled

<Autocomplete items={fruits} disabled>
  <AutocompleteInput placeholder="Disabled autocomplete" />
  <AutocompleteContent>
    <AutocompleteList>
      {(fruit: Fruit) => (
        <AutocompleteItem key={fruit.id} value={fruit}>
          {fruit.value}
        </AutocompleteItem>
      )}
    </AutocompleteList>
  </AutocompleteContent>
</Autocomplete>

Open On Input Click

<Autocomplete items={fruits} openOnClick>
  <AutocompleteInput placeholder="Click to open..." />
  <AutocompleteContent>
    <AutocompleteList>
      {(fruit: Fruit) => (
        <AutocompleteItem key={fruit.id} value={fruit}>
          {fruit.value}
        </AutocompleteItem>
      )}
    </AutocompleteList>
  </AutocompleteContent>
</Autocomplete>

With Input Group

import { Search } from "lucide-react";

<div className="relative">
  <Search className="absolute left-3 top-3 size-4 text-muted-foreground" />
  <Autocomplete items={fruits}>
    <AutocompleteInput className="pl-9" placeholder="Search..." />
    <AutocompleteContent>
      <AutocompleteList>
        {(fruit: Fruit) => (
          <AutocompleteItem key={fruit.id} value={fruit}>
            {fruit.value}
          </AutocompleteItem>
        )}
      </AutocompleteList>
    </AutocompleteContent>
  </Autocomplete>
</div>
import { useState, useEffect } from "react";
import { AutocompleteStatus } from "@/components/ui/autocomplete";

function AsyncAutocomplete() {
  const [items, setItems] = useState<Fruit[]>([]);
  const [loading, setLoading] = useState(false);
  const [query, setQuery] = useState("");

  useEffect(() => {
    if (query) {
      setLoading(true);
      // Simulate API call
      setTimeout(() => {
        const filtered = fruits.filter((f) =>
          f.value.toLowerCase().includes(query.toLowerCase())
        );
        setItems(filtered);
        setLoading(false);
      }, 300);
    } else {
      setItems([]);
    }
  }, [query]);

  return (
    <Autocomplete items={items}>
      <AutocompleteInput
        placeholder="Type to search..."
        value={query}
        onValueChange={setQuery}
      />
      <AutocompleteContent>
        <AutocompleteStatus>
          {loading ? "Loading..." : null}
        </AutocompleteStatus>
        <AutocompleteEmpty>No results found.</AutocompleteEmpty>
        <AutocompleteList>
          {(fruit: Fruit) => (
            <AutocompleteItem key={fruit.id} value={fruit}>
              {fruit.value}
            </AutocompleteItem>
          )}
        </AutocompleteList>
      </AutocompleteContent>
    </Autocomplete>
  );
}

Limit Results

import { useFilter } from "@/components/ui/autocomplete";

function LimitedAutocomplete() {
  const filter = useFilter();
  const maxResults = 5;

  return (
    <Autocomplete items={fruits} filter={filter}>
      <AutocompleteInput placeholder="Type to search..." />
      <AutocompleteContent>
        <AutocompleteEmpty>No results found.</AutocompleteEmpty>
        <AutocompleteList>
          {(fruit: Fruit, index: number) =>
            index < maxResults ? (
              <AutocompleteItem key={fruit.id} value={fruit}>
                {fruit.value}
              </AutocompleteItem>
            ) : null
          }
        </AutocompleteList>
      </AutocompleteContent>
    </Autocomplete>
  );
}

With Custom Filtering

import { useFilter } from "@/components/ui/autocomplete";

function CustomFilterAutocomplete() {
  const customFilter = useFilter({
    matchOption: (option, query) => {
      // Custom fuzzy matching logic
      return option.value.toLowerCase().includes(query.toLowerCase());
    },
  });

  return (
    <Autocomplete items={fruits} filter={customFilter}>
      <AutocompleteInput placeholder="Type to search..." />
      <AutocompleteContent>
        <AutocompleteList>
          {(fruit: Fruit) => (
            <AutocompleteItem key={fruit.id} value={fruit}>
              {fruit.value}
            </AutocompleteItem>
          )}
        </AutocompleteList>
      </AutocompleteContent>
    </Autocomplete>
  );
}

API Reference

Autocomplete

Extends all props from @base-ui/react Autocomplete.Root component.
items
array
required
Array of items for suggestions.
value
any
The controlled selected value.
defaultValue
any
The default selected value when uncontrolled.
onValueChange
function
Callback fired when the selected value changes.
open
boolean
Controlled open state of the popup.
onOpenChange
function
Callback when popup open state changes.
openOnClick
boolean
default:"false"
Whether to open the popup when clicking the input.
disabled
boolean
Whether the autocomplete is disabled.
filter
function
Custom filter function for items. Use useFilter hook.

AutocompleteInput

placeholder
string
Placeholder text for the input.
value
string
Controlled input value.
onValueChange
function
Callback when input value changes.
render
ReactNode
Custom render element. Defaults to Input component.
className
string
Additional CSS classes to apply.

AutocompleteContent

sideOffset
number
default:"6"
Distance in pixels from the input.
className
string
Additional CSS classes to apply.

AutocompleteItem

value
any
required
The value of the suggestion item.
disabled
boolean
Whether the item is disabled.
className
string
Additional CSS classes to apply.

AutocompleteList

Container for autocomplete items with scroll support.

AutocompleteEmpty

Displayed when no items match the input.

AutocompleteStatus

Displays status messages (e.g., loading state).

useFilter

Hook for creating custom filter functions.
const filter = useFilter({
  matchOption: (option, query) => boolean,
});

TypeScript

import { Autocomplete as AutocompletePrimitive } from "@base-ui/react";

const Autocomplete = AutocompletePrimitive.Root;
const useFilter = AutocompletePrimitive.useFilter;

type AutocompleteInputProps = React.ComponentProps<typeof AutocompletePrimitive.Input>;
type AutocompleteItemProps = React.ComponentProps<typeof AutocompletePrimitive.Item>;

Accessibility

  • Keyboard navigation (Arrow keys, Enter, Escape)
  • Proper ARIA attributes
  • Screen reader announcements
  • Focus management
  • Type-ahead support
  • Combobox - Similar with selection focus
  • Input - Basic text input
  • Select - Dropdown without search

Build docs developers (and LLMs) love