Combobox

Search and select from a list without leaving the field.

fieldsearch
Preview
Combobox preview

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

No starter chosen yet.

Keyboard interactions

KeysAction
ArrowDown / ArrowUpOpens the list, then moves through enabled options, wrapping at the ends.
EnterSelects the active option.
EscapeCloses 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.