1
ComponentsForm Controls

Autocomplete

Remote user suggestions and other async results while a user types, with free-text input for search and tagging.

Purpose

Autocomplete combines free-text input with filtered suggestions. Use it for search, tagging, and command-like input where the typed value is meaningful. Use Select for a short closed list and Combobox when selection is constrained to listed options.

Suggestion input

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

export function Example() {
  return (
    <Autocomplete>
      <AutocompleteInput aria-label="Member" placeholder="Search members" showTrigger />
      <AutocompletePopup>
        <AutocompleteList>
          <AutocompleteItem value="maya">Maya Chen</AutocompleteItem>
        </AutocompleteList>
      </AutocompletePopup>
    </Autocomplete>
  );
}

Installation

With the Siteplane registry alias configured, add the component and import the installed source from your app.

CLI

pnpm dlx shadcn@4.16.1 add @siteplane/autocomplete

Import

import { Autocomplete, AutocompleteEmpty, AutocompleteGroup, AutocompleteGroupLabel, AutocompleteInput, AutocompleteItem, AutocompleteList, AutocompletePopup, AutocompleteRow, AutocompleteStatus, useAutocompleteFilter } from "@/components/ui/autocomplete";

Need the machine-readable source metadata? View registry JSON.

Usage

Use Autocomplete for suggestions while typing. Use the input group, popup, list and empty state together.

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

export function Example() {
  return (
    <Autocomplete>
      <AutocompleteInput aria-label="Member" placeholder="Search members" showTrigger />
      <AutocompletePopup>
        <AutocompleteList>
          <AutocompleteItem value="maya">Maya Chen</AutocompleteItem>
        </AutocompleteList>
      </AutocompletePopup>
    </Autocomplete>
  );
}

Examples

Clear button, form fields, groups, icons, external filtering, sizes, states and status hints are props and compositions of the same primitive.

With clear button

<Autocomplete items={frameworks}>
  <AutocompleteInput
    aria-label="Search frameworks"
    placeholder="Search frameworks"
    showClear
  />
  <AutocompletePopup>
    <AutocompleteEmpty>No results</AutocompleteEmpty>
    <AutocompleteList>
      {(item: string) => (
        <AutocompleteItem key={item} value={item}>{item}</AutocompleteItem>
      )}
    </AutocompleteList>
  </AutocompletePopup>
</Autocomplete>

Grouped items

<Autocomplete items={groupedFrameworks}>
  <AutocompleteInput aria-label="Search grouped frameworks" placeholder="Search frameworks" />
  <AutocompletePopup>
    <AutocompleteEmpty>No results</AutocompleteEmpty>
    <AutocompleteList>
      {(group: GroupedFrameworks) => (
        <Fragment key={group.value}>
          <AutocompleteGroup items={group.items}>
            <AutocompleteGroupLabel>{group.value}</AutocompleteGroupLabel>
            {group.items.map((item) => (
              <AutocompleteItem key={item} value={item}>{item}</AutocompleteItem>
            ))}
          </AutocompleteGroup>
          <AutocompleteSeparator />
        </Fragment>
      )}
    </AutocompleteList>
  </AutocompletePopup>
</Autocomplete>

With icons and start addon

<Autocomplete
  itemToStringValue={(item: AreaOption) => item.value}
  items={areaOptions}
>
  <AutocompleteInput
    aria-label="Search areas"
    placeholder="Search areas"
    startAddon={<SearchIcon />}
  />
  <AutocompletePopup>
    <AutocompleteEmpty>No results</AutocompleteEmpty>
    <AutocompleteList>
      {(item: AreaOption) => (
        <AutocompleteItem className="gap-2" key={item.value} value={item}>
          <span aria-hidden="true">{item.icon}</span>
          {item.value}
        </AutocompleteItem>
      )}
    </AutocompleteList>
  </AutocompletePopup>
</Autocomplete>

External filtering (command-like)

const [value, setValue] = useState("");
const filter = useAutocompleteFilter();

const filteredItems = useMemo(
  () => commands.filter((item) => filter.contains(item, value)),
  [filter, value],
);

<Autocomplete filteredItems={filteredItems} onValueChange={setValue} value={value}>
  <AutocompleteInput
    aria-label="Search commands"
    placeholder="Search commands"
    startAddon={<SearchIcon />}
  />
  <AutocompletePopup>
    <AutocompleteEmpty>No results</AutocompleteEmpty>
    <AutocompleteList>
      {(item: string) => (
        <AutocompleteItem key={item} value={item}>{item}</AutocompleteItem>
      )}
    </AutocompleteList>
  </AutocompletePopup>
</Autocomplete>

Sizes

<AutocompleteInput size="sm" ... />
<AutocompleteInput ... />
<AutocompleteInput size="lg" ... />
<AutocompleteInput size="xl" ... />

In a form field

Typing filters the list live.

Framework: not submitted
const labelId = useId();

<Form onSubmit={onSubmit}>
  <Field name="framework">
    <FieldLabel id={labelId}>Framework</FieldLabel>
    <Autocomplete items={frameworks} name="framework">
      <AutocompleteInput
        aria-labelledby={labelId}
        placeholder="Search frameworks"
      />
      <AutocompletePopup>
        <AutocompleteEmpty>No results</AutocompleteEmpty>
        <AutocompleteList>
          {(item: string) => (
            <AutocompleteItem key={item} value={item}>{item}</AutocompleteItem>
          )}
        </AutocompleteList>
      </AutocompletePopup>
    </Autocomplete>
    <FieldDescription>Typing filters the list live.</FieldDescription>
  </Field>
  <Button loading={loading} type="submit">Submit</Button>
</Form>

States

{/* Disabled */}
<Autocomplete items={frameworks}>
  <AutocompleteInput
    aria-label="Search frameworks"
    disabled
    placeholder="Search frameworks"
  />
  <AutocompletePopup>
    <AutocompleteEmpty>No results</AutocompleteEmpty>
    <AutocompleteList>
      {(item: string) => (
        <AutocompleteItem key={item} value={item}>{item}</AutocompleteItem>
      )}
    </AutocompleteList>
  </AutocompletePopup>
</Autocomplete>

{/* Invalid */}
<Autocomplete items={frameworks}>
  <AutocompleteInput
    aria-invalid
    aria-label="Search frameworks"
    placeholder="Search frameworks"
  />
  <AutocompletePopup>
    <AutocompleteEmpty>No results</AutocompleteEmpty>
    <AutocompleteList>
      {(item: string) => (
        <AutocompleteItem key={item} value={item}>{item}</AutocompleteItem>
      )}
    </AutocompleteList>
  </AutocompletePopup>
</Autocomplete>

With status

<Autocomplete items={frameworks}>
  <AutocompleteInput aria-label="Search frameworks" placeholder="Search frameworks" />
  <AutocompletePopup>
    <AutocompleteStatus>Type to filter frameworks.</AutocompleteStatus>
    <AutocompleteEmpty>No results</AutocompleteEmpty>
    <AutocompleteList>
      {(item: string) => (
        <AutocompleteItem key={item} value={item}>{item}</AutocompleteItem>
      )}
    </AutocompleteList>
  </AutocompletePopup>
</Autocomplete>

API Reference

The reference lists the source-owned exports and props that are easy to miss in visual examples. Inherited Base UI props remain available unless the wrapper narrows them.

  • Render AutocompleteInput, AutocompletePopup, AutocompleteList, and AutocompleteItem together.
  • mode accepts "list", "both", "inline", or "none" and controls how typed text is completed.
  • AutocompletePopup accepts side, align, sideOffset, and alignOffset; its defaults are side="bottom" and align="start".
  • AutocompleteItem disabled represents an unavailable suggestion.
  • AutocompleteList provides the bounded ScrollArea, scrollbar gutter, and scroll fade used by long result sets.
  • AutocompleteRow supports grid-style result layouts.
  • AutocompleteStatus is the status line for loading and result messages; AutocompleteEmpty renders the no-results state.
  • Filter through root items or useAutocompleteFilter with filteredItems.

Motion

Component motion uses the shared Siteplane motion contract. See the global motion guide for provider setup, tokens and reduced-motion behavior.

  • Popup scale and fade, highlighted-item states, and reduced-motion behavior are native to the primitive.
  • Filtering, highlighting, status, and empty-state changes do not resize the input.
  • Interactive suggestions and icon actions use a pointer cursor; the text input keeps its text cursor.

Accessibility

  • Give every AutocompleteInput an accessible name through a visible label, aria-label, or aria-labelledby.
  • Keep disabled suggestions unavailable to pointer and keyboard selection.
  • Connect invalid state to the input and render a visible FieldError.
  • The popup remains keyboard navigable and closes on Escape or outside click.

Implementation Guidance

  • Always compose the input, popup, list, and items; do not render a raw input without its suggestion list.
  • Use Field for a visible label, description, and validation.
  • Preserve native filtering, highlight, cursor, popup, and motion behavior.

On This Page