GitHub

Combobox

Searchable picker for larger option lists with keyboard navigation support.

01 Live Demo

expand_more

02 Code Snippet

src/ui/Combobox.tsx
import { Combobox } from "@/ui/Combobox";

<Combobox.Root
  value={value}
  onValueChange={setValue}
  options={[
    { label: "React", value: "react" },
    { label: "Vue", value: "vue" },
    { label: "Svelte", value: "svelte" },
  ]}
  placeholder="Search framework..."
/>

03 Copy-Paste (Single File)

Combobox.tsx
import { useMemo, useState } from "react";

type Option = { label: string; value: string };

function ComboboxRoot({ options, value, onValueChange, placeholder = "Search..." }: { options: Option[]; value?: string; onValueChange?: (value: string) => void; placeholder?: string }) {
  const [query, setQuery] = useState("");
  const [open, setOpen] = useState(false);
  const filtered = useMemo(() => options.filter((o) => o.label.toLowerCase().includes(query.toLowerCase())), [options, query]);

  return (
    <div className="relative">
      <input
        value={query}
        onChange={(event) => { setQuery(event.target.value); setOpen(true); }}
        onFocus={() => setOpen(true)}
        placeholder={placeholder}
        className="h-10 w-full rounded-lg border border-slate-200 px-3.5 text-sm"
      />
      {open && (
        <div className="absolute z-40 mt-2 w-full rounded-xl border border-slate-200 bg-white p-1 shadow-xl">
          {filtered.map((option) => (
            <button key={option.value} className="w-full rounded-lg px-3 py-2 text-left text-sm hover:bg-slate-50" onClick={() => { onValueChange?.(option.value); setQuery(option.label); setOpen(false); }}>
              {option.label} {value === option.value ? "✓" : ""}
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

type ComboboxCompound = typeof ComboboxRoot & { Root: typeof ComboboxRoot };
export const Combobox = Object.assign(ComboboxRoot, { Root: ComboboxRoot }) as ComboboxCompound;
react-principles