An input with a filterable list of options — pick one, or several as chips.
Nothing selected yet.
'use client'
import * as React from 'react'
import {
Combobox,
ComboboxInput,
ComboboxContent,
ComboboxEmpty,
ComboboxList,
ComboboxItem,
} from '@/components/ui/combobox'
import { Label } from '@/components/ui/label'
interface Framework {
value: string
label: string
}
const frameworks: Framework[] = [
{ value: 'next', label: 'Next.js' },
{ value: 'sveltekit', label: 'SvelteKit' },
{ value: 'nuxt', label: 'Nuxt' },
{ value: 'remix', label: 'Remix' },
{ value: 'astro', label: 'Astro' },
{ value: 'solidstart', label: 'SolidStart' },
{ value: 'qwik', label: 'Qwik City' },
{ value: 'eleventy', label: 'Eleventy' },
]
export default function ComboboxDemo() {
const [value, setValue] = React.useState<Framework | null>(null)
return (
<div className="grid w-full max-w-xs gap-3">
<Combobox items={frameworks} value={value} onValueChange={setValue}>
<div className="grid gap-1.5">
<Label htmlFor="combobox-framework">Framework</Label>
<ComboboxInput id="combobox-framework" placeholder="Search frameworks…" showClear />
</div>
<ComboboxContent>
<ComboboxEmpty>No framework found.</ComboboxEmpty>
<ComboboxList>
{(item: Framework) => (
<ComboboxItem key={item.value} value={item}>
{item.label}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
<p className="text-sm text-muted-foreground" aria-live="polite">
{value ? `Selected: ${value.label}` : 'Nothing selected yet.'}
</p>
</div>
)
}
Built on Base UI
Installation
pnpm dlx shadcn@latest add https://kagadmodyaa.vercel.app/r/combobox.jsonNeeds a React + Tailwind project set up with shadcn. Copies the component source straight into your project, so you can edit it.
Combobox imports cn() from lib/utils.ts — add it too if you don't have it yet:
import { clsx, type ClassValue } from 'clsx'
import { twMerge } from 'tailwind-merge'
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
Usage
import {
Combobox,
ComboboxInput,
ComboboxContent,
ComboboxEmpty,
ComboboxList,
ComboboxItem,
} from '@/components/ui/combobox'
const frameworks = [
{ value: 'next', label: 'Next.js' },
{ value: 'astro', label: 'Astro' },
]
<Combobox items={frameworks} onValueChange={choose}>
<ComboboxInput aria-label="Framework" placeholder="Search frameworks…" />
<ComboboxContent>
<ComboboxEmpty>No framework found.</ComboboxEmpty>
<ComboboxList>
{item => (
<ComboboxItem key={item.value} value={item}>
{item.label}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>Examples
Multiple selection
TypeScript
Python
'use client'
import {
Combobox,
ComboboxValue,
ComboboxChips,
ComboboxChip,
ComboboxChipsInput,
ComboboxContent,
ComboboxEmpty,
ComboboxList,
ComboboxItem,
} from '@/components/ui/combobox'
import { Label } from '@/components/ui/label'
interface Language {
value: string
label: string
}
const languages: Language[] = [
{ value: 'ts', label: 'TypeScript' },
{ value: 'js', label: 'JavaScript' },
{ value: 'py', label: 'Python' },
{ value: 'go', label: 'Go' },
{ value: 'rust', label: 'Rust' },
{ value: 'swift', label: 'Swift' },
{ value: 'kotlin', label: 'Kotlin' },
{ value: 'ruby', label: 'Ruby' },
]
export default function ComboboxMultiple() {
return (
<Combobox items={languages} multiple defaultValue={[languages[0], languages[2]]}>
<div className="grid w-full max-w-sm gap-1.5">
<Label htmlFor="combobox-languages">Languages</Label>
<ComboboxChips>
<ComboboxValue>
{(selected: Language[]) => (
<>
{selected.map(language => (
<ComboboxChip key={language.value}>{language.label}</ComboboxChip>
))}
<ComboboxChipsInput
id="combobox-languages"
placeholder={selected.length > 0 ? '' : 'Pick languages…'}
/>
</>
)}
</ComboboxValue>
</ComboboxChips>
</div>
<ComboboxContent>
<ComboboxEmpty>No language found.</ComboboxEmpty>
<ComboboxList>
{(item: Language) => (
<ComboboxItem key={item.value} value={item}>
{item.label}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
)
}
API Reference
| Name | Type | Default | Description |
|---|---|---|---|
| items | Item[] | { value: string; items: Item[] }[] | — | The options, flat or in groups. Objects shaped { value, label } are searched and shown by label. Filtering is built in. |
| value / onValueChange | Item | null / (value) => void | — | Controlled selection. With multiple, the value is an array. |
| defaultValue | Item | Item[] | null | — | Initial selection (uncontrolled). |
| multiple | boolean | false | Lets several options be selected. Render the field with ComboboxChips. |
| inputValue / onInputValueChange | string / (value: string) => void | — | Control the text in the field. |
| filter | (item, query, itemToString?) => boolean | — | Replace the default match (case- and accent-insensitive "contains"). Pass null to disable filtering. |
| itemToStringLabel | (item: Item) => string | — | Turns an item into its label when it is not shaped { value, label }. |
| isItemEqualToValue | (item: Item, value: Item) => boolean | — | Compare items that are equal but not the same object. |
| open / onOpenChange | boolean / (open: boolean) => void | — | Control whether the list is open. |
| disabled / readOnly / required | boolean | false | Standard form-control states. Also accepts name for form submission. |
| ComboboxInput showTrigger | boolean | true | Shows the chevron button that opens the list. |
| ComboboxInput showClear | boolean | false | Shows a button that clears the selection while one is set. |
| ComboboxContent side / align / sideOffset | 'top' | 'bottom' / 'start' | 'center' | 'end' / number | 'bottom' / 'start' / 6 | Where the list appears relative to the field. It is as wide as the field. |
| ComboboxList children | (item, index) => ReactNode | — | A render function, called for each visible option. |
| ComboboxEmpty | — | — | Shown only while nothing matches. Render it before ComboboxList. |
| ComboboxChips / ComboboxChip / ComboboxChipsInput | — | — | The field for multiple selection: render a chip per selected value and the input after them. Backspace removes the last chip; the left arrow moves onto the chips. |
| Keyboard | — | — | ArrowDown opens the list and moves the highlight, Enter selects, Escape closes, and typing filters. |