Combobox
A Select you can type into: the query filters by label and keywords (prefix matches first), arrows move, Enter picks. multiple keeps the list open and shows picks as removable tags. Submits through hidden inputs.
Type to filter 10 channels.
Try “mod” or “owner”.
"use client";import { Hash } from "lucide-react";import { useState } from "react";import { Combobox } from "@/components/ui/combobox";import { Field } from "@/components/ui/field";export function ChannelPicker({ channels }: { channels: string[] }) { const [channel, setChannel] = useState(""); return ( <Field label="Post clips to"> <Combobox name="channel" options={channels.map((c) => ({ value: c, label: c, icon: <Hash /> }))} value={channel} onValueChange={setChannel} placeholder="Pick a channel…" /> </Field> );}"use client";import { useState } from "react";import { Combobox } from "@/components/ui/combobox";const roles = [ { value: "admin", label: "Admin", keywords: ["owner"] }, { value: "moderator", label: "Moderator", keywords: ["mod"] }, { value: "member", label: "Member" },];export function RolePicker() { const [picked, setPicked] = useState<string[]>([]); return ( <Combobox multiple aria-label="Roles" options={roles} value={picked} onValueChange={setPicked} placeholder="Add roles…" /> );}<Combobox>
Select you can type into: the input filters the list, Up and Down move, Enter picks, Esc closes. Single shows the pick in the input; multiple shows picks as tags and keeps the list open.
options*ComboboxOption[]Choices; filtered as the user types.
aria-describedbystringIds of hint or error text; Field fills it in.
aria-invalidbooleanError styling; Field sets it from
error.aria-labelstringAccessible name when there is no visible label.
defaultValuestring | string[]Value, or values when
multiple, at first render (uncontrolled).disabledbooleanBlocks typing and opening.
emptyTextReactNodeShown in the list when nothing matches.
idstringId of the input; Field fills it in.
multiplebooleanPicks stay open and show as removable tags; Backspace removes the last one.
namestringSubmits the value(s) through hidden inputs (one per value when multiple).
onValueChange((value: string) => void) | ((value: string[]) => void)Called with the picked value, or every picked value when
multiple.placeholderstringField text when nothing is picked.
requiredbooleanForm submit needs a value.
valuestring | string[]Controlled value, or values when
multiple; pair withonValueChange.
| Prop | Type | Default |
|---|---|---|
options*Choices; filtered as the user types. | ComboboxOption[] | — |
aria-describedbyIds of hint or error text; Field fills it in. | string | — |
aria-invalidError styling; Field sets it from | boolean | — |
aria-labelAccessible name when there is no visible label. | string | — |
defaultValueValue, or values when | string | string[] | — |
disabledBlocks typing and opening. | boolean | — |
emptyTextShown in the list when nothing matches. | ReactNode | — |
idId of the input; Field fills it in. | string | — |
multiplePicks stay open and show as removable tags; Backspace removes the last one. | boolean | — |
nameSubmits the value(s) through hidden inputs (one per value when multiple). | string | — |
onValueChangeCalled with the picked value, or every picked value when | ((value: string) => void) | ((value: string[]) => void) | — |
placeholderField text when nothing is picked. | string | — |
requiredForm submit needs a value. | boolean | — |
valueControlled value, or values when | string | string[] | — |
- type ComboboxOption
- { value: string; label: string; icon?: ReactNode; keywords?: string[]; disabled?: boolean; }
* required. Every component also takes className.