---
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 React 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.