---
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
} from "@/components/ui/combobox"
const frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"]
---
<Combobox>
<ComboboxInput placeholder="Select a framework" />
<ComboboxContent>
<ComboboxList>
<ComboboxEmpty>No items found.</ComboboxEmpty>
{frameworks.map((item) => (
<ComboboxItem value={item}>{item}</ComboboxItem>
))}
</ComboboxList>
</ComboboxContent>
</Combobox>Installation
npx shadcn@latest add @fulldev/combobox
Usage
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
} from "@/components/ui/combobox"
<Combobox>
<ComboboxInput placeholder="Select a framework" />
<ComboboxContent>
<ComboboxList>
<ComboboxEmpty>No items found.</ComboboxEmpty>
<ComboboxItem value="Next.js">Next.js</ComboboxItem>
<ComboboxItem value="SvelteKit">SvelteKit</ComboboxItem>
<ComboboxItem value="Astro">Astro</ComboboxItem>
</ComboboxList>
</ComboboxContent>
</Combobox>
Items are authored markup, so render a data array with .map(). Data Slot
filters items by their label and selects one value at a time.
Composition
Place ComboboxEmpty inside ComboboxList, as required by Data Slot.
Combobox
├── ComboboxInput
└── ComboboxContent
└── ComboboxList
├── ComboboxEmpty
├── ComboboxItem
└── ComboboxItem
Put ComboboxSeparator between groups as a direct child of ComboboxList:
Data Slot only shows separators there, and hides them when a filter leaves no
items on one side.
Combobox
├── ComboboxInput
└── ComboboxContent
└── ComboboxList
├── ComboboxEmpty
├── ComboboxGroup
│ ├── ComboboxLabel
│ └── ComboboxItem
├── ComboboxSeparator
└── ComboboxGroup
├── ComboboxLabel
└── ComboboxItem
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"]
---
<Combobox>
<ComboboxInput placeholder="Select a framework" />
<ComboboxContent>
<ComboboxList>
<ComboboxEmpty>No items found.</ComboboxEmpty>
{frameworks.map((item) => (
<ComboboxItem value={item}>{item}</ComboboxItem>
))}
</ComboboxList>
</ComboboxContent>
</Combobox>Multiple
The Astro combobox has no multiple selection or chips, so the official Multiple and RTL examples, which both use chips, are in the shadcn/ui Combobox docs only.
Clear Button
Use the showClear prop on ComboboxInput 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"]
---
<Combobox defaultValue={frameworks[0]}>
<ComboboxInput placeholder="Select a framework" showClear />
<ComboboxContent>
<ComboboxList>
<ComboboxEmpty>No items found.</ComboboxEmpty>
{frameworks.map((item) => (
<ComboboxItem value={item}>{item}</ComboboxItem>
))}
</ComboboxList>
</ComboboxContent>
</Combobox>Groups
Use ComboboxGroup and ComboboxSeparator to group items.
---
import {
Combobox,
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",
],
},
]
---
<Combobox>
<ComboboxInput placeholder="Select a timezone" />
<ComboboxContent>
<ComboboxList>
<ComboboxEmpty>No timezones found.</ComboboxEmpty>
{timezones.map((group, index) => (
<>
<ComboboxGroup>
<ComboboxLabel>{group.value}</ComboboxLabel>
{group.items.map((item) => (
<ComboboxItem value={item}>{item}</ComboboxItem>
))}
</ComboboxGroup>
{index < timezones.length - 1 && <ComboboxSeparator />}
</>
))}
</ComboboxList>
</ComboboxContent>
</Combobox>Custom Items
You can render a custom component inside ComboboxItem. Set data-label on
the item to the text used for filtering and for the input after selection.
---
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",
},
]
---
<Combobox>
<ComboboxInput placeholder="Search countries..." />
<ComboboxContent>
<ComboboxList>
<ComboboxEmpty>No countries found.</ComboboxEmpty>
{countries
.filter((country) => country.code !== "")
.map((country) => (
<ComboboxItem value={country.value} data-label={country.label}>
<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 on ComboboxInput 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"]
---
<Combobox>
<ComboboxInput placeholder="Select a framework" aria-invalid="true" />
<ComboboxContent>
<ComboboxList>
<ComboboxEmpty>No items found.</ComboboxEmpty>
{frameworks.map((item) => (
<ComboboxItem value={item}>{item}</ComboboxItem>
))}
</ComboboxList>
</ComboboxContent>
</Combobox>Disabled
Use the disabled prop on ComboboxInput to disable the input and its
trigger, or on Combobox to disable all interaction.
---
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
} from "@/components/ui/combobox"
const frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"]
---
<Combobox>
<ComboboxInput placeholder="Select a framework" disabled />
<ComboboxContent>
<ComboboxList>
<ComboboxEmpty>No items found.</ComboboxEmpty>
{frameworks.map((item) => (
<ComboboxItem 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"]
---
<Combobox autoHighlight>
<ComboboxInput placeholder="Select a framework" />
<ComboboxContent>
<ComboboxList>
<ComboboxEmpty>No items found.</ComboboxEmpty>
{frameworks.map((item) => (
<ComboboxItem value={item}>{item}</ComboboxItem>
))}
</ComboboxList>
</ComboboxContent>
</Combobox>Popup
Trigger the combobox from any button with data-slot="combobox-trigger" that
contains a ComboboxValue, and move ComboboxInput inside ComboboxContent
with showTrigger={false}.
---
import ChevronDownIcon from "@lucide/astro/icons/chevron-down"
import { Button } from "@/components/ui/button"
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
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",
},
]
---
<Combobox defaultValue={countries[0].value}>
<Button
variant="outline"
data-slot="combobox-trigger"
class="cn-combobox-trigger w-64 justify-between font-normal"
>
<ComboboxValue />
<ChevronDownIcon class="cn-combobox-trigger-icon pointer-events-none" />
</Button>
<ComboboxContent>
<ComboboxInput showTrigger={false} placeholder="Search" />
<ComboboxList>
<ComboboxEmpty>No items found.</ComboboxEmpty>
{countries.map((item) => (
<ComboboxItem value={item.value}>{item.label}</ComboboxItem>
))}
</ComboboxList>
</ComboboxContent>
</Combobox>Input Group
Add an addon by placing an InputGroupAddon inside ComboboxInput.
---
import GlobeIcon from "@lucide/astro/icons/globe"
import {
Combobox,
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",
],
},
]
---
<Combobox>
<ComboboxInput placeholder="Select a timezone">
<InputGroupAddon>
<GlobeIcon />
</InputGroupAddon>
</ComboboxInput>
<ComboboxContent alignOffset={-28} class="w-60">
<ComboboxList>
<ComboboxEmpty>No timezones found.</ComboboxEmpty>
{timezones.map((group) => (
<ComboboxGroup>
<ComboboxLabel>{group.value}</ComboboxLabel>
{group.items.map((item) => (
<ComboboxItem value={item}>{item}</ComboboxItem>
))}
</ComboboxGroup>
))}
</ComboboxList>
</ComboboxContent>
</Combobox>API Reference
Source: src/components/ui/combobox
Interactivity comes from
@data-slot/combobox.
| Component | Prop | Notes |
|---|---|---|
Combobox | defaultValue, defaultOpen, placeholder, disabled, required, name | name adds a hidden input that submits the value |
Combobox | openOnFocus, autoHighlight | openOnFocus defaults to true |
Combobox | side, align, sideOffset, alignOffset, avoidCollisions, collisionPadding | Popup placement, also accepted on ComboboxContent |
ComboboxInput | showTrigger (true), showClear (false), disabled, input attributes | Extra children render inside the input group |
ComboboxItem | value, disabled, showIndicator (true), data-label | Without data-label, the item text is the label |
ComboboxTrigger | showIcon (true) | Ghost icon button with a chevron |
ComboboxValue | span attributes | Shows the selected label, or placeholder when empty |
The root emits combobox:change, combobox:open-change and
combobox:input-change, and accepts combobox:set with value, open or
inputValue in detail.