Combobox
Search and select from a list without leaving the field.
fieldsearch
Preview
Combobox preview
Live · keyboard ready
When to use
- Single-choice fields with long lists, such as time zones or countries.
- Lists where people know the name and want to type, including aliases via keywords.
When not to use
- Use select for a short list where typing adds nothing.
- Use multi-select when several values can be chosen.
- Use expanding-search when the search navigates instead of setting a value.
shadcn CLI
Also installs (1)
- manicat-foundation
Manual install(+)
registry.jsonjson
{
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
"name": "combobox",
"type": "registry:ui",
"title": "Combobox",
"description": "Search and select from a list without leaving the field.",
"categories": [
"inputs"
],
"dependencies": [
"lucide-react",
"motion"
],
"registryDependencies": [
"https://manicat.viraza.net/r/manicat-foundation.json"
],
"files": [
{
"path": "registry/components/combobox/combobox.tsx",
"type": "registry:component",
"target": "@components/manicat/combobox/combobox.tsx"
},
{
"path": "registry/components/combobox/combobox.module.css",
"type": "registry:component",
"target": "@components/manicat/combobox/combobox.module.css"
}
],
"docs": "Docs and live preview: https://uiarc.dev/components/combobox",
"meta": {
"tier": "free",
"kind": "component",
"docs": "https://manicat.viraza.net/components/combobox",
"markdown": "https://uiarc.dev/components/combobox/markdown",
"tags": [
"field",
"search"
]
}
}Usage
Install the component, then copy this into your app. Change the content and the props to fit your screen.
example.tsxtsx
"use client";
import { useState } from "react";
import { Combobox } from "@/components/manicat/combobox/combobox";
export function ComboboxExample() {
const [value, setValue] = useState("");
return (
<Combobox
label="Framework"
placeholder="Choose a framework"
value={value}
onValueChange={setValue}
options={[
{ value: "next", label: "Next.js", keywords: ["react", "app router"] },
{ value: "vite", label: "Vite", keywords: ["react", "bundler"] },
{ value: "astro", label: "Astro", keywords: ["islands"] },
{ value: "waku", label: "Waku", keywords: ["react"] },
]}
/>
);
}
Examples
Real screens rather than isolated props. Switch to Code to copy a whole example into your project.
Framework picker A searchable list where keywords help: typing “react” also finds Next.js and Vite.
Framework picker
A searchable list where keywords help: typing “react” also finds Next.js and Vite. preview
Live · keyboard ready
No starter chosen yet.
framework-picker-a-searchable-list-where-keywords-help-typing-react-also-finds-next-js-and-vite-.tsxtsx
"use client";
import { useState } from "react";
import { Combobox } from "@/components/manicat/combobox/combobox";
export function ComboboxFrameworkExample() {
const [value, setValue] = useState("");
return (
<div className="grid w-full max-w-xs gap-2">
<Combobox
label="Framework"
placeholder="Choose a framework"
value={value}
onValueChange={setValue}
options={[
{ value: "next", label: "Next.js", keywords: ["react", "app router"] },
{ value: "vite", label: "Vite", keywords: ["react", "bundler"] },
{ value: "astro", label: "Astro", keywords: ["islands"] },
{ value: "waku", label: "Waku", keywords: ["react"] },
]}
/>
<p className="text-sm text-(color:--text-muted)">{value ? `Starter: ${value}` : "No starter chosen yet."}</p>
</div>
);
}
Keyboard interactions
| Keys | Action |
|---|---|
| ArrowDown / ArrowUp | Opens the list, then moves through enabled options, wrapping at the ends. |
| Enter | Selects the active option. |
| Escape | Closes the list and discards the query. |
Accessibility
- Input has role="combobox" with aria-expanded, aria-controls, aria-autocomplete="list", and aria-activedescendant.
- Options use role="option" with aria-selected and aria-disabled; the list is labelled "<label> options".
- The empty state is a role="status" row; the clear button is labelled "Clear selection".
Motion
- The popover springs in from slightly above and the listbox height follows filtering on a smooth spring.
- A chosen label rises into the field with a soft blur; the clear button scales in.
- Reduced motion keeps opacity-only fades and instant height changes.
Responsive behavior
- The popover spans the field width and the listbox caps at min(300px, 40vh), scrolling with contained overscroll.
- It closes on any pointerdown outside, so a tap elsewhere dismisses it on touch.
Performance
- Filtering is memoized and runs on every keystroke across all options; results are not virtualized, so pre-filter very large lists.
- A ResizeObserver drives the listbox height spring while filtering.
Notes for AI
- Use for long or searchable single-choice lists. Use select for short lists, multi-select for several values, and expanding-search for search that navigates.
- Controlled with value and onValueChange, or uncontrolled with defaultValue. The input shows the label, not the value, so pair name with a hidden input if you post a form.
- Add keywords to options for synonyms and aliases.