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
| Prop | Type | Description |
|---|---|---|
options | AutocompleteOption[] | 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) => void | Called when an option is chosen via click or Enter. |
placeholder | string | Input placeholder; also used as the combobox/listbox accessible name. |
debounceMs | number | Delay before filtering (sync) or fetching (async) after the last keystroke. |
minChars | number | Minimum characters before a search runs. Below this, results are cleared and hintMessage (if set) shows. |
emptyMessage | string | Shown when a search completes with zero matches. |
loadingMessage | string | Shown in async mode while onSearch is pending. |
hintMessage | string | Shown when the query length is below minChars. Only rendered if provided. |
errorMessage | string | Shown when onSearch rejects. |
theme | "light" | "dark" | Theme for default input and dropdown colors. |
className | string | Additional classes merged onto the root wrapper. |