- Accordion
- Alert
- Alert Dialog
- Aspect Ratio
- Attachment
- Avatar
- Badge
- Breadcrumb
- Bubble
- Button
- Button Group
- Card
- Checkbox
- Collapsible
- Combobox
- Context Menu
- Date Picker (Lite)
- Dialog
- Direction
- Drawer
- Dropdown Menu
- Empty
- Field
- Hover Card
- Input
- Input Group
- Input OTP (Lite)
- Item
- Kbd
- Label
- Marker
- Menubar
- Message
- Native Select
- Navigation Menu
- Pagination
- Popover
- Progress
- Radio Group
- Scroll Area
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Spinner
- Switch
- Table
- Tabs
- Textarea
- Toast
- Toggle
- Toggle Group
- Tooltip
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
} from "@/components/ui/combobox"
const frameworks = [
"Next.js",
"SvelteKit",
"Nuxt.js",
"Remix",
"Astro",
] as const
export default function ComboboxBasic() {
return (
<Combobox items={frameworks}>
<ComboboxInput placeholder="Select a framework" />
<ComboboxContent>
<ComboboxEmpty>No items found.</ComboboxEmpty>
<ComboboxList>
{(item) => (
<ComboboxItem key={item} value={item}>
{item}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
)
}Installation
pnpm dlx shadcnui-hono-jsx@latest add comboboxnpx shadcnui-hono-jsx@latest add comboboxyarn dlx shadcnui-hono-jsx@latest add comboboxbunx shadcnui-hono-jsx@latest add comboboxThis component uses a small client script. add installs it into public/shadcn/; serve that directory at /shadcn/ and load the script on pages that use the component (see Client Scripts):
<script type="module" src="/shadcn/combobox.js"></script>
<script type="module" src="/shadcn/input-group.js"></script>Usage
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
} from "@/components/ui/combobox"const frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"]
export function ExampleCombobox() {
return (
<Combobox items={frameworks}>
<ComboboxInput placeholder="Select a framework" />
<ComboboxContent>
<ComboboxEmpty>No items found.</ComboboxEmpty>
<ComboboxList>
{(item) => (
<ComboboxItem key={item} value={item}>
{item}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
)
}Composition
Simple
A single-line input and a flat list (see Basic).
Combobox
├── ComboboxInput
└── ComboboxContent
├── ComboboxEmpty
└── ComboboxList
├── ComboboxItem
└── ComboboxItemWith chips
Multi-select with multiple, chips, and a chips input (see Multiple).
Combobox
├── ComboboxChips
│ ├── ComboboxValue
│ │ └── ComboboxChip
│ └── ComboboxChipsInput
└── ComboboxContent
├── ComboboxEmpty
└── ComboboxList
├── ComboboxItem
└── ComboboxItemWith groups and collection
Nested items per group using ComboboxCollection inside each ComboboxGroup, with a separator between groups (see Groups).
Combobox
├── ComboboxInput
└── ComboboxContent
├── ComboboxEmpty
└── ComboboxList
├── ComboboxGroup
│ ├── ComboboxLabel
│ └── ComboboxCollection
│ ├── ComboboxItem
│ └── ComboboxItem
├── ComboboxSeparator
└── ComboboxGroup
├── ComboboxLabel
└── ComboboxCollection
├── ComboboxItem
└── ComboboxItemCustom Items
Use itemToStringValue when your items are objects.
import * as React from "react"
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
} from "@/components/ui/combobox"
type Framework = {
label: string
value: string
}
const frameworks: Framework[] = [
{ label: "Next.js", value: "next" },
{ label: "SvelteKit", value: "sveltekit" },
{ label: "Nuxt", value: "nuxt" },
]
export function ExampleComboboxCustomItems() {
return (
<Combobox
items={frameworks}
itemToStringValue={(framework) => framework.label}
>
<ComboboxInput placeholder="Select a framework" />
<ComboboxContent>
<ComboboxEmpty>No items found.</ComboboxEmpty>
<ComboboxList>
{(framework) => (
<ComboboxItem key={framework.value} value={framework}>
{framework.label}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
)
}Multiple Selection
Use multiple with chips for multi-select behavior.
import * as React from "react"
import {
Combobox,
ComboboxChip,
ComboboxChips,
ComboboxChipsInput,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
ComboboxValue,
} from "@/components/ui/combobox"
const frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"]
export function ExampleComboboxMultiple() {
const [value, setValue] = React.useState<string[]>([])
return (
<Combobox
items={frameworks}
multiple
value={value}
onValueChange={setValue}
>
<ComboboxChips>
<ComboboxValue>
{value.map((item) => (
<ComboboxChip key={item}>{item}</ComboboxChip>
))}
</ComboboxValue>
<ComboboxChipsInput placeholder="Add framework" />
</ComboboxChips>
<ComboboxContent>
<ComboboxEmpty>No items found.</ComboboxEmpty>
<ComboboxList>
{(item) => (
<ComboboxItem key={item} value={item}>
{item}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
)
}Basic
A simple combobox with a list of frameworks.
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
} from "@/components/ui/combobox"
const frameworks = [
"Next.js",
"SvelteKit",
"Nuxt.js",
"Remix",
"Astro",
] as const
export default function ComboboxBasic() {
return (
<Combobox items={frameworks}>
<ComboboxInput placeholder="Select a framework" />
<ComboboxContent>
<ComboboxEmpty>No items found.</ComboboxEmpty>
<ComboboxList>
{(item) => (
<ComboboxItem key={item} value={item}>
{item}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
)
}Multiple
A combobox with multiple selection using multiple and ComboboxChips.
import { Fragment } from "hono/jsx"
import {
Combobox,
ComboboxChip,
ComboboxChips,
ComboboxChipsInput,
ComboboxContent,
ComboboxEmpty,
ComboboxItem,
ComboboxList,
ComboboxValue,
useComboboxAnchor,
} from "@/components/ui/combobox"
const frameworks = [
"Next.js",
"SvelteKit",
"Nuxt.js",
"Remix",
"Astro",
] as const
export function ComboboxMultiple() {
const anchor = useComboboxAnchor()
return (
<Combobox
multiple
autoHighlight
items={frameworks}
defaultValue={[frameworks[0]]}
>
<ComboboxChips ref={anchor} class="w-full max-w-xs">
<ComboboxValue>
{(values) => (
<Fragment>
{values.map((value: string) => (
<ComboboxChip key={value}>{value}</ComboboxChip>
))}
<ComboboxChipsInput />
</Fragment>
)}
</ComboboxValue>
</ComboboxChips>
<ComboboxContent anchor={anchor}>
<ComboboxEmpty>No items found.</ComboboxEmpty>
<ComboboxList>
{(item) => (
<ComboboxItem key={item} value={item}>
{item}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
)
}Clear Button
Use the showClear prop to show a clear button.
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
} from "@/components/ui/combobox"
const frameworks = [
"Next.js",
"SvelteKit",
"Nuxt.js",
"Remix",
"Astro",
] as const
export function ComboboxWithClear() {
return (
<Combobox items={frameworks} defaultValue={frameworks[0]}>
<ComboboxInput placeholder="Select a framework" showClear />
<ComboboxContent>
<ComboboxEmpty>No items found.</ComboboxEmpty>
<ComboboxList>
{(item) => (
<ComboboxItem key={item} value={item}>
{item}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
)
}Groups
Use ComboboxGroup and ComboboxSeparator to group items.
import {
Combobox,
ComboboxCollection,
ComboboxContent,
ComboboxEmpty,
ComboboxGroup,
ComboboxInput,
ComboboxItem,
ComboboxLabel,
ComboboxList,
ComboboxSeparator,
} from "@/components/ui/combobox"
const timezones = [
{
value: "Americas",
items: [
"(GMT-5) New York",
"(GMT-8) Los Angeles",
"(GMT-6) Chicago",
"(GMT-5) Toronto",
"(GMT-8) Vancouver",
"(GMT-3) São Paulo",
],
},
{
value: "Europe",
items: [
"(GMT+0) London",
"(GMT+1) Paris",
"(GMT+1) Berlin",
"(GMT+1) Rome",
"(GMT+1) Madrid",
"(GMT+1) Amsterdam",
],
},
{
value: "Asia/Pacific",
items: [
"(GMT+9) Tokyo",
"(GMT+8) Shanghai",
"(GMT+8) Singapore",
"(GMT+4) Dubai",
"(GMT+11) Sydney",
"(GMT+9) Seoul",
],
},
] as const
export function ComboboxWithGroupsAndSeparator() {
return (
<Combobox items={timezones}>
<ComboboxInput placeholder="Select a timezone" />
<ComboboxContent>
<ComboboxEmpty>No timezones found.</ComboboxEmpty>
<ComboboxList>
{(group, index) => (
<ComboboxGroup key={group.value} items={group.items}>
<ComboboxLabel>{group.value}</ComboboxLabel>
<ComboboxCollection>
{(item) => (
<ComboboxItem key={item} value={item}>
{item}
</ComboboxItem>
)}
</ComboboxCollection>
{index < timezones.length - 1 && <ComboboxSeparator />}
</ComboboxGroup>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
)
}Custom Items
You can render a custom component inside ComboboxItem.
South America (ar)
Oceania (au)
South America (br)
North America (ca)
Asia (cn)
South America (co)
Africa (eg)
Europe (fr)
Europe (de)
Europe (it)
Asia (jp)
Africa (ke)
North America (mx)
Oceania (nz)
Africa (ng)
Africa (za)
Asia (kr)
Europe (gb)
North America (us)
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
} from "@/components/ui/combobox"
import {
Item,
ItemContent,
ItemDescription,
ItemTitle,
} from "@/components/ui/item"
const countries = [
{ code: "", value: "", continent: "", label: "Select country" },
{
code: "ar",
value: "argentina",
label: "Argentina",
continent: "South America",
},
{ code: "au", value: "australia", label: "Australia", continent: "Oceania" },
{ code: "br", value: "brazil", label: "Brazil", continent: "South America" },
{ code: "ca", value: "canada", label: "Canada", continent: "North America" },
{ code: "cn", value: "china", label: "China", continent: "Asia" },
{
code: "co",
value: "colombia",
label: "Colombia",
continent: "South America",
},
{ code: "eg", value: "egypt", label: "Egypt", continent: "Africa" },
{ code: "fr", value: "france", label: "France", continent: "Europe" },
{ code: "de", value: "germany", label: "Germany", continent: "Europe" },
{ code: "it", value: "italy", label: "Italy", continent: "Europe" },
{ code: "jp", value: "japan", label: "Japan", continent: "Asia" },
{ code: "ke", value: "kenya", label: "Kenya", continent: "Africa" },
{ code: "mx", value: "mexico", label: "Mexico", continent: "North America" },
{
code: "nz",
value: "new-zealand",
label: "New Zealand",
continent: "Oceania",
},
{ code: "ng", value: "nigeria", label: "Nigeria", continent: "Africa" },
{
code: "za",
value: "south-africa",
label: "South Africa",
continent: "Africa",
},
{ code: "kr", value: "south-korea", label: "South Korea", continent: "Asia" },
{
code: "gb",
value: "united-kingdom",
label: "United Kingdom",
continent: "Europe",
},
{
code: "us",
value: "united-states",
label: "United States",
continent: "North America",
},
]
export function ComboboxWithCustomItems() {
return (
<Combobox
items={countries.filter((country) => country.code !== "")}
itemToStringValue={(country: (typeof countries)[number]) => country.label}
>
<ComboboxInput placeholder="Search countries..." />
<ComboboxContent>
<ComboboxEmpty>No countries found.</ComboboxEmpty>
<ComboboxList>
{(country) => (
<ComboboxItem key={country.code} value={country}>
<Item size="xs" class="p-0">
<ItemContent>
<ItemTitle class="whitespace-nowrap">
{country.label}
</ItemTitle>
<ItemDescription>
{country.continent} ({country.code})
</ItemDescription>
</ItemContent>
</Item>
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
)
}Invalid
Use the aria-invalid prop to make the combobox invalid.
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
} from "@/components/ui/combobox"
const frameworks = [
"Next.js",
"SvelteKit",
"Nuxt.js",
"Remix",
"Astro",
] as const
export function ComboboxInvalid() {
return (
<Combobox items={frameworks}>
<ComboboxInput placeholder="Select a framework" aria-invalid="true" />
<ComboboxContent>
<ComboboxEmpty>No items found.</ComboboxEmpty>
<ComboboxList>
{(item) => (
<ComboboxItem key={item} value={item}>
{item}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
)
}Disabled
Use the disabled prop to disable the combobox.
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
} from "@/components/ui/combobox"
const frameworks = [
"Next.js",
"SvelteKit",
"Nuxt.js",
"Remix",
"Astro",
] as const
export function ComboboxDisabled() {
return (
<Combobox items={frameworks}>
<ComboboxInput placeholder="Select a framework" disabled />
<ComboboxContent>
<ComboboxEmpty>No items found.</ComboboxEmpty>
<ComboboxList>
{(item) => (
<ComboboxItem key={item} value={item}>
{item}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
)
}Auto Highlight
Use the autoHighlight prop to automatically highlight the first item on filter.
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
} from "@/components/ui/combobox"
const frameworks = [
"Next.js",
"SvelteKit",
"Nuxt.js",
"Remix",
"Astro",
] as const
export function ComboboxAutoHighlight() {
return (
<Combobox items={frameworks} autoHighlight>
<ComboboxInput placeholder="Select a framework" />
<ComboboxContent>
<ComboboxEmpty>No items found.</ComboboxEmpty>
<ComboboxList>
{(item) => (
<ComboboxItem key={item} value={item}>
{item}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
)
}Popup
You can trigger the combobox from a button or any other component by using the render prop. Move the ComboboxInput inside the ComboboxContent.
import { Button } from "@/components/ui/button"
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
ComboboxTrigger,
ComboboxValue,
} from "@/components/ui/combobox"
const countries = [
{ code: "", value: "", continent: "", label: "Select country" },
{
code: "ar",
value: "argentina",
label: "Argentina",
continent: "South America",
},
{ code: "au", value: "australia", label: "Australia", continent: "Oceania" },
{ code: "br", value: "brazil", label: "Brazil", continent: "South America" },
{ code: "ca", value: "canada", label: "Canada", continent: "North America" },
{ code: "cn", value: "china", label: "China", continent: "Asia" },
{
code: "co",
value: "colombia",
label: "Colombia",
continent: "South America",
},
{ code: "eg", value: "egypt", label: "Egypt", continent: "Africa" },
{ code: "fr", value: "france", label: "France", continent: "Europe" },
{ code: "de", value: "germany", label: "Germany", continent: "Europe" },
{ code: "it", value: "italy", label: "Italy", continent: "Europe" },
{ code: "jp", value: "japan", label: "Japan", continent: "Asia" },
{ code: "ke", value: "kenya", label: "Kenya", continent: "Africa" },
{ code: "mx", value: "mexico", label: "Mexico", continent: "North America" },
{
code: "nz",
value: "new-zealand",
label: "New Zealand",
continent: "Oceania",
},
{ code: "ng", value: "nigeria", label: "Nigeria", continent: "Africa" },
{
code: "za",
value: "south-africa",
label: "South Africa",
continent: "Africa",
},
{ code: "kr", value: "south-korea", label: "South Korea", continent: "Asia" },
{
code: "gb",
value: "united-kingdom",
label: "United Kingdom",
continent: "Europe",
},
{
code: "us",
value: "united-states",
label: "United States",
continent: "North America",
},
]
export function ComboboxPopup() {
return (
<>
<Combobox items={countries} defaultValue={countries[0]}>
<ComboboxTrigger
render={
<Button
variant="outline"
class="w-64 justify-between font-normal"
/>
}
>
<ComboboxValue />
</ComboboxTrigger>
<ComboboxContent>
<ComboboxInput showTrigger={false} placeholder="Search" />
<ComboboxEmpty>No items found.</ComboboxEmpty>
<ComboboxList>
{(item) => (
<ComboboxItem key={item.code} value={item}>
{item.label}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
</>
)
}Input Group
You can add an addon to the combobox by using the InputGroupAddon component inside the ComboboxInput.
import { GlobeIcon } from "@/components/icons"
import {
Combobox,
ComboboxCollection,
ComboboxContent,
ComboboxEmpty,
ComboboxGroup,
ComboboxInput,
ComboboxItem,
ComboboxLabel,
ComboboxList,
} from "@/components/ui/combobox"
import { InputGroupAddon } from "@/components/ui/input-group"
const timezones = [
{
value: "Americas",
items: [
"(GMT-5) New York",
"(GMT-8) Los Angeles",
"(GMT-6) Chicago",
"(GMT-5) Toronto",
"(GMT-8) Vancouver",
"(GMT-3) São Paulo",
],
},
{
value: "Europe",
items: [
"(GMT+0) London",
"(GMT+1) Paris",
"(GMT+1) Berlin",
"(GMT+1) Rome",
"(GMT+1) Madrid",
"(GMT+1) Amsterdam",
],
},
{
value: "Asia/Pacific",
items: [
"(GMT+9) Tokyo",
"(GMT+8) Shanghai",
"(GMT+8) Singapore",
"(GMT+4) Dubai",
"(GMT+11) Sydney",
"(GMT+9) Seoul",
],
},
] as const
export function ComboxboxInputGroup() {
return (
<Combobox items={timezones}>
<ComboboxInput placeholder="Select a timezone">
<InputGroupAddon>
<GlobeIcon />
</InputGroupAddon>
</ComboboxInput>
<ComboboxContent alignOffset={-28} class="w-60">
<ComboboxEmpty>No timezones found.</ComboboxEmpty>
<ComboboxList>
{(group) => (
<ComboboxGroup key={group.value} items={group.items}>
<ComboboxLabel>{group.value}</ComboboxLabel>
<ComboboxCollection>
{(item) => (
<ComboboxItem key={item} value={item}>
{item}
</ComboboxItem>
)}
</ComboboxCollection>
</ComboboxGroup>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
)
}RTL
To enable RTL support in shadcn/ui, see the RTL configuration guide.
// This example sets dir and uses useTranslation because this site is not RTL.
// In an RTL app you won't need them; see the RTL guide.
import { Fragment } from "hono/jsx"
import {
type Translations,
useTranslation,
} from "@/components/language-selector"
import {
Combobox,
ComboboxChip,
ComboboxChips,
ComboboxChipsInput,
ComboboxContent,
ComboboxEmpty,
ComboboxItem,
ComboboxList,
ComboboxValue,
useComboboxAnchor,
} from "@/components/ui/combobox"
import { Field, FieldLabel } from "@/components/ui/field"
const categories = [
"technology",
"design",
"business",
"marketing",
"education",
"health",
] as const
const translations: Translations = {
en: {
dir: "ltr",
values: {
label: "Categories",
placeholder: "Add categories",
empty: "No categories found.",
technology: "Technology",
design: "Design",
business: "Business",
marketing: "Marketing",
education: "Education",
health: "Health",
},
},
ar: {
dir: "rtl",
values: {
label: "الفئات",
placeholder: "أضف فئات",
empty: "لم يتم العثور على فئات.",
technology: "التكنولوجيا",
design: "التصميم",
business: "الأعمال",
marketing: "التسويق",
education: "التعليم",
health: "الصحة",
},
},
he: {
dir: "rtl",
values: {
label: "קטגוריות",
placeholder: "הוסף קטגוריות",
empty: "לא נמצאו קטגוריות.",
technology: "טכנולוגיה",
design: "עיצוב",
business: "עסקים",
marketing: "שיווק",
education: "חינוך",
health: "בריאות",
},
},
}
export function ComboboxRtl() {
const { dir, t, language } = useTranslation(translations, "ar")
const anchor = useComboboxAnchor()
const categoryLabels: Record<string, string> = {
technology: t.technology,
design: t.design,
business: t.business,
marketing: t.marketing,
education: t.education,
health: t.health,
}
return (
<Field class="mx-auto w-full max-w-xs">
<FieldLabel>{t.label}</FieldLabel>
<Combobox
multiple
autoHighlight
items={categories}
defaultValue={[categories[0]]}
itemToStringValue={(item: (typeof categories)[number]) =>
categoryLabels[item] || item
}
>
<ComboboxChips ref={anchor}>
<ComboboxValue>
{(values) => (
<Fragment>
{values.map((value: string) => (
<ComboboxChip key={value}>
{categoryLabels[value] || value}
</ComboboxChip>
))}
<ComboboxChipsInput placeholder={t.placeholder} />
</Fragment>
)}
</ComboboxValue>
</ComboboxChips>
<ComboboxContent
anchor={anchor}
dir={dir}
data-lang={dir === "rtl" ? language : undefined}
>
<ComboboxEmpty>{t.empty}</ComboboxEmpty>
<ComboboxList>
{(item) => (
<ComboboxItem key={item} value={item}>
{categoryLabels[item] || item}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
</Field>
)
}Notes
- Opening, filtering, keyboard highlighting and selection need the client script
/shadcn/combobox.js(<script type="module" src="/shadcn/combobox.js">); without it the input shows the initial selection and the form submits the initial value. The list is a native popover placed with CSS anchor positioning. - Items come from
itemsand theComboboxListrender function (or static children); values are compared by their string form (itemToStringValue, a{ label, value }object's value, or the string). Filtering follows Base UI's default (contains, ignoring case, accents and punctuation);filter,limit,filteredItemsandinlineare not supported. New chips of a multiple combobox are copied from theComboboxValuerender function's output for the first item, with its label replaced. Controlled state (open,onValueChange,inputValue) is not supported. - Accepts
classinstead ofclassName. useComboboxAnchor()returns a CSS anchor name forComboboxChips(ref) andComboboxContent(anchor) instead of a React ref.- Upstream documentation: shadcn/ui Combobox. See also Differences from shadcn/ui.
API Reference
See the Base UI documentation for more information.