Autocomplete Search

Accessible combobox with debounced filtering, async-ready search, full keyboard navigation, grouped options, matched-text highlighting, and loading / empty / error states.

UniqueUI CLI

npx uniqueui add autocomplete-search

shadcn CLI

npx shadcn@latest add https://uniqueui-platform.vercel.app/r/autocomplete-search.json -y

shadcn path expects @/lib/utils (run shadcn init first). Same source file is installed to components/ui/.

Static filtering

Variant 1 of 3
"use client";
import {
  AutocompleteSearch,
  type AutocompleteOption,
} from "@/components/ui/autocomplete-search";

const frameworks: AutocompleteOption[] = [
  { value: "next", label: "Next.js" },
  { value: "remix", label: "Remix" },
  { value: "astro", label: "Astro" },
  { value: "svelte", label: "SvelteKit" },
  { value: "nuxt", label: "Nuxt" },
  { value: "solid", label: "SolidStart" },
  { value: "vite", label: "Vite" },
  { value: "gatsby", label: "Gatsby" },
];

export default function Example() {
  return (
    <div className="flex h-[440px] w-full justify-center px-6 pt-10">
      <AutocompleteSearch
        options={frameworks}
        placeholder="Search frameworks…"
        theme="dark"
      />
    </div>
  );
}

Grouped options

Variant 2 of 3
"use client";
import {
  AutocompleteSearch,
  type AutocompleteOption,
} from "@/components/ui/autocomplete-search";

const commands: AutocompleteOption[] = [
  { value: "new-file", label: "New File", group: "Actions" },
  { value: "new-folder", label: "New Folder", group: "Actions" },
  { value: "save-all", label: "Save All", group: "Actions" },
  { value: "profile", label: "Profile", group: "Account" },
  { value: "billing", label: "Billing", group: "Account" },
  { value: "logout", label: "Log out", group: "Account" },
  { value: "docs", label: "Documentation", group: "Help" },
  { value: "shortcuts", label: "Keyboard Shortcuts", group: "Help" },
];

export default function Example() {
  return (
    <div className="flex h-[440px] w-full justify-center px-6 pt-10">
      <AutocompleteSearch
        options={commands}
        placeholder="Type a command…"
        theme="dark"
      />
    </div>
  );
}

Async remote search

Variant 3 of 3
"use client";
import {
  AutocompleteSearch,
  type AutocompleteOption,
} from "@/components/ui/autocomplete-search";

const countries: AutocompleteOption[] = [
  { value: "us", label: "United States", description: "North America" },
  { value: "ca", label: "Canada", description: "North America" },
  { value: "mx", label: "Mexico", description: "North America" },
  { value: "br", label: "Brazil", description: "South America" },
  { value: "ar", label: "Argentina", description: "South America" },
  { value: "gb", label: "United Kingdom", description: "Europe" },
  { value: "de", label: "Germany", description: "Europe" },
  { value: "fr", label: "France", description: "Europe" },
  { value: "jp", label: "Japan", description: "Asia" },
  { value: "in", label: "India", description: "Asia" },
  { value: "au", label: "Australia", description: "Oceania" },
];

// Swap this for a real fetch() to your API.
function searchCountries(query: string) {
  return new Promise<AutocompleteOption[]>((resolve) => {
    const needle = query.toLowerCase();
    setTimeout(() => {
      resolve(
        countries.filter((c) => c.label.toLowerCase().includes(needle)),
      );
    }, 600);
  });
}

export default function Example() {
  return (
    <div className="flex h-[440px] w-full justify-center px-6 pt-10">
      <AutocompleteSearch
        onSearch={searchCountries}
        minChars={1}
        placeholder="Search countries…"
        hintMessage="Type to search countries"
        loadingMessage="Searching countries…"
        theme="dark"
      />
    </div>
  );
}

Props

PropTypeDescription
optionsAutocompleteOption[]Static option list for synchronous local filtering. Ignored when onSearch is provided.
onSearch(query: string) => Promise<AutocompleteOption[]>Async resolver returning matches for a query. Enables async mode with loading/error states and a stale-response guard.
onSelect(option: AutocompleteOption) => voidCalled when an option is chosen via click or Enter.
placeholderstringInput placeholder; also used as the combobox/listbox accessible name.
debounceMsnumberDelay before filtering (sync) or fetching (async) after the last keystroke.
minCharsnumberMinimum characters before a search runs. Below this, results are cleared and hintMessage (if set) shows.
emptyMessagestringShown when a search completes with zero matches.
loadingMessagestringShown in async mode while onSearch is pending.
hintMessagestringShown when the query length is below minChars. Only rendered if provided.
errorMessagestringShown when onSearch rejects.
theme"light" | "dark"Theme for default input and dropdown colors.
classNamestringAdditional classes merged onto the root wrapper.