ZaidanHomeDocsComponentsBlocksChartsTypesetCreate

Command Palette

Search for a command to run...

GitHub
New
Sections
  • Introduction
  • Components
  • Blocks
  • Installation
  • Customization
  • Dark Mode
  • Typeset
  • Zaidan Skills
  • FAQ
  • Roadmap
  • Changelog
Installation
  • Astro
  • Manual
  • Solid Start
  • TanStack Router
  • TanStack Start
  • Vite
Blocks
  • Data Grid
  • Event Calendar
  • Filters
  • Gantt
  • Image Crop
  • Kanban
  • Message Scroller
  • Questionnaire
  • Sortable
Components
  • Accordion
  • Alert
  • Alert Dialog
  • Aspect Ratio
  • Attachment
  • Avatar
  • Badge
  • Breadcrumb
  • Bubble
  • Button
  • Button Group
  • Calendar
  • Card
  • Carousel
  • Chart
  • Checkbox
  • Collapsible
  • Combobox
  • Command
  • Context Menu
  • Dialog
  • Drawer
  • Dropdown Menu
  • Empty
  • Field
  • Hover Card
  • Input
  • Input Group
  • Input OTP
  • Item
  • Kbd
  • Label
  • Marker
  • Menubar
  • Message
  • Native Select
  • Navigation Menu
  • Pagination
  • Popover
  • Progress
  • Radio Group
  • Resizable
  • Scroll Area
  • Select
  • Separator
  • Sheet
  • Sidebar
  • Skeleton
  • Slider
  • Spinner
  • Switch
  • Table
  • Tabs
  • Textarea
  • Toast
  • Toggle
  • Toggle Group
  • Tooltip

Combobox

Autocomplete input with a list of suggestions.

Kobalte
Kobalte

The Combobox component is built on top of Kobalte Combobox, which provides the accessible autocomplete primitive and keyboard navigation.

1
import {
2
Combobox,
3
ComboboxContent,
4
ComboboxEmpty,
5
ComboboxInput,
6
ComboboxItem,
7
} from "~/components/ui/combobox";
8
9
const frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"];
10
11
export default function ComboboxDemo() {
12
return (
13
<Combobox
14
options={frameworks}
15
placeholder="Select a framework..."
16
itemComponent={(props) => (
17
<ComboboxItem item={props.item}>{props.item.rawValue}</ComboboxItem>
18
)}
19
>
20
<ComboboxInput placeholder="Select a framework..." />
21
<ComboboxContent>
22
<ComboboxEmpty>No frameworks found.</ComboboxEmpty>
23
</ComboboxContent>
24
</Combobox>
25
);
26
}

Installation

pnpm dlx shadcn@latest add @zaidan/combobox
npx shadcn@latest add @zaidan/combobox
yarn dlx shadcn@latest add @zaidan/combobox
bunx --bun shadcn@latest add @zaidan/combobox

Usage

1
import {
2
Combobox,
3
ComboboxContent,
4
ComboboxEmpty,
5
ComboboxInput,
6
ComboboxItem,
7
} from "~/components/ui/combobox";
8
9
const frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"];
1
<Combobox
2
options={frameworks}
3
placeholder="Select a framework..."
4
itemComponent={(props) => (
5
<ComboboxItem item={props.item}>{props.item.rawValue}</ComboboxItem>
6
)}
7
>
8
<ComboboxInput placeholder="Select a framework..." />
9
<ComboboxContent>
10
<ComboboxEmpty>No frameworks found.</ComboboxEmpty>
11
</ComboboxContent>
12
</Combobox>

See the Kobalte Combobox documentation for primitive-level options, accessibility, and keyboard behavior.

Composition

Simple

A single-line input and a flat list rendered from options via itemComponent (see Basic).

Combobox
├── ComboboxInput
└── ComboboxContent
└── ComboboxEmpty

With Chips

Combobox
├── ComboboxChips
│ ├── ComboboxValue
│ │ └── ComboboxChip
│ └── ComboboxChipsInput
└── ComboboxContent

With Groups

Group options with optionGroupChildren, and render a section heading (and an optional separator) from sectionComponent (see Groups).

Combobox
├── ComboboxInput
└── ComboboxContent
└── ComboboxEmpty
sectionComponent renders, per group:
├── ComboboxSeparator (optional, skipped for the first group)
└── ComboboxSection
└── ComboboxSectionLabel

Custom Items

Use optionValue and optionTextValue when your options are objects.

1
type Framework = {
2
label: string;
3
value: string;
4
};
5
6
const frameworks: Framework[] = [
7
{ label: "Next.js", value: "next" },
8
{ label: "SvelteKit", value: "sveltekit" },
9
{ label: "Nuxt", value: "nuxt" },
10
];
11
12
<Combobox
13
options={frameworks}
14
optionValue="value"
15
optionTextValue="label"
16
placeholder="Select a framework..."
17
itemComponent={(props) => (
18
<ComboboxItem item={props.item}>{props.item.rawValue.label}</ComboboxItem>
19
)}
20
>
21
<ComboboxInput placeholder="Select a framework..." />
22
<ComboboxContent>
23
<ComboboxEmpty>No frameworks found.</ComboboxEmpty>
24
</ComboboxContent>
25
</Combobox>

Multiple Selection

Use multiple with ComboboxChips, ComboboxValue, ComboboxChip, and ComboboxChipsInput, following the shadcn composition. Selected chips remain visible while filtering or opening and closing the list.

1
import { For } from "solid-js";
2
import {
3
Combobox,
4
ComboboxChip,
5
ComboboxChips,
6
ComboboxChipsInput,
7
ComboboxContent,
8
ComboboxItem,
9
ComboboxValue,
10
} from "~/components/ui/combobox";
11
12
const frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"];
13
14
<Combobox
15
options={frameworks}
16
multiple
17
defaultValue={[frameworks[0]]}
18
itemComponent={(props) => (
19
<ComboboxItem item={props.item}>{props.item.rawValue}</ComboboxItem>
20
)}
21
>
22
<ComboboxChips>
23
<ComboboxValue<string>>
24
{(values) => (
25
<For each={values}>
26
{(value) => <ComboboxChip value={value} removeLabel={`Remove ${value}`}>{value}</ComboboxChip>}
27
</For>
28
)}
29
</ComboboxValue>
30
<ComboboxChipsInput placeholder="Add framework..." />
31
</ComboboxChips>
32
<ComboboxContent />
33
</Combobox>

Pass the selected option as each chip's value: Kobalte removes options by value rather than by their rendered position. ComboboxChips also anchors the popup through Kobalte Control, so no useComboboxAnchor hook is needed.

ComboboxValue provides the selected options to its render function. Pass explicit chip children and a removeLabel for the remove button’s accessible name. Set showRemove={false} to hide a chip's remove button. The remove buttons have accessible names and support keyboard activation; removal returns focus to the input.

Use value and onChange for controlled selection, or defaultValue for uncontrolled selection. Set disabled or validationState="invalid" on Combobox to apply those states to the chips input. Disabled selections stay visible and cannot be removed.

Basic

A simple combobox with a list of frameworks.

1
import {
2
Combobox,
3
ComboboxContent,
4
ComboboxEmpty,
5
ComboboxInput,
6
ComboboxItem,
7
} from "~/components/ui/combobox";
8
9
const frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"];
10
11
export default function ComboboxBasic() {
12
return (
13
<Combobox
14
options={frameworks}
15
placeholder="Select a framework..."
16
itemComponent={(props) => (
17
<ComboboxItem item={props.item}>{props.item.rawValue}</ComboboxItem>
18
)}
19
>
20
<ComboboxInput placeholder="Select a framework..." />
21
<ComboboxContent>
22
<ComboboxEmpty>No frameworks found.</ComboboxEmpty>
23
</ComboboxContent>
24
</Combobox>
25
);
26
}

Multiple

A combobox with multiple selection using multiple and ComboboxChips, starting with a removable Next.js chip.

Next.js
1
import { For } from "solid-js";
2
import {
3
Combobox,
4
ComboboxChip,
5
ComboboxChips,
6
ComboboxChipsInput,
7
ComboboxContent,
8
ComboboxEmpty,
9
ComboboxItem,
10
ComboboxValue,
11
} from "~/components/ui/combobox";
12
13
const frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"];
14
15
export default function ComboboxMultiple() {
16
return (
17
<Combobox<(typeof frameworks)[number]>
18
options={frameworks}
19
placeholder="Select frameworks..."
20
multiple
21
triggerMode="focus"
22
defaultValue={[frameworks[0]]}
23
itemComponent={(props) => (
24
<ComboboxItem item={props.item}>{props.item.rawValue}</ComboboxItem>
25
)}
26
>
27
<ComboboxChips class="w-full max-w-xs">
28
<ComboboxValue<string>>
29
{(values) => (
30
<For each={values}>
31
{(value) => (
32
<ComboboxChip value={value} removeLabel={`Remove ${value}`}>
33
{value}
34
</ComboboxChip>
35
)}
36
</For>
37
)}
38
</ComboboxValue>
39
<ComboboxChipsInput placeholder="Add framework..." />
40
</ComboboxChips>
41
<ComboboxContent>
42
<ComboboxEmpty>No frameworks found.</ComboboxEmpty>
43
</ComboboxContent>
44
</Combobox>
45
);
46
}

Clear Button

Use the showClear prop to show a clear button.

1
import {
2
Combobox,
3
ComboboxContent,
4
ComboboxEmpty,
5
ComboboxInput,
6
ComboboxItem,
7
} from "~/components/ui/combobox";
8
9
const frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"];
10
11
export default function ComboboxClear() {
12
return (
13
<Combobox
14
options={frameworks}
15
placeholder="Select a framework..."
16
defaultValue={frameworks[0]}
17
itemComponent={(props) => (
18
<ComboboxItem item={props.item}>{props.item.rawValue}</ComboboxItem>
19
)}
20
>
21
<ComboboxInput placeholder="Select a framework..." showClear />
22
<ComboboxContent>
23
<ComboboxEmpty>No frameworks found.</ComboboxEmpty>
24
</ComboboxContent>
25
</Combobox>
26
);
27
}

Groups

Use optionGroupChildren with a sectionComponent rendering ComboboxSection, ComboboxSectionLabel, and ComboboxSeparator to group items.

1
import { Show } from "solid-js";
2
import {
3
Combobox,
4
ComboboxContent,
5
ComboboxEmpty,
6
ComboboxInput,
7
ComboboxItem,
8
ComboboxSection,
9
ComboboxSectionLabel,
10
ComboboxSeparator,
11
} from "~/components/ui/combobox";
12
13
type TimezoneGroup = {
14
label: string;
15
options: string[];
16
};
17
18
const timezones: TimezoneGroup[] = [
19
{
20
label: "Americas",
21
options: [
22
"(GMT-5) New York",
23
"(GMT-8) Los Angeles",
24
"(GMT-6) Chicago",
25
"(GMT-5) Toronto",
26
"(GMT-8) Vancouver",
27
"(GMT-3) São Paulo",
28
],
29
},
30
{
31
label: "Europe",
32
options: [
33
"(GMT+0) London",
34
"(GMT+1) Paris",
35
"(GMT+1) Berlin",
36
"(GMT+1) Rome",
37
"(GMT+1) Madrid",
38
"(GMT+1) Amsterdam",
39
],
40
},
41
{
42
label: "Asia/Pacific",
43
options: [
44
"(GMT+9) Tokyo",
45
"(GMT+8) Shanghai",
46
"(GMT+8) Singapore",
47
"(GMT+4) Dubai",
48
"(GMT+11) Sydney",
49
"(GMT+9) Seoul",
50
],
51
},
52
];
53
54
export default function ComboboxGroups() {
55
return (
56
<Combobox<string, TimezoneGroup>
57
options={timezones}
58
optionValue={(option) => option}
59
optionTextValue={(option) => option}
60
optionGroupChildren="options"
61
placeholder="Select a timezone..."
62
itemComponent={(props) => (
63
<ComboboxItem item={props.item}>{props.item.rawValue}</ComboboxItem>
64
)}
65
sectionComponent={(props) => (
66
<>
67
<Show when={props.section.index !== 0}>
68
<ComboboxSeparator />
69
</Show>
70
<ComboboxSection>
71
<ComboboxSectionLabel>{props.section.rawValue.label}</ComboboxSectionLabel>
72
</ComboboxSection>
73
</>
74
)}
75
>
76
<ComboboxInput placeholder="Select a timezone..." />
77
<ComboboxContent>
78
<ComboboxEmpty>No timezones found.</ComboboxEmpty>
79
</ComboboxContent>
80
</Combobox>
81
);
82
}

Custom Items

You can render a custom component inside ComboboxItem via itemComponent.

1
import {
2
Combobox,
3
ComboboxContent,
4
ComboboxEmpty,
5
ComboboxInput,
6
ComboboxItem,
7
} from "~/components/ui/combobox";
8
import { Item, ItemContent, ItemDescription, ItemTitle } from "~/components/ui/item";
9
10
const countries = [
11
{ code: "ar", value: "argentina", label: "Argentina", continent: "South America" },
12
{ code: "au", value: "australia", label: "Australia", continent: "Oceania" },
13
{ code: "br", value: "brazil", label: "Brazil", continent: "South America" },
14
{ code: "ca", value: "canada", label: "Canada", continent: "North America" },
15
{ code: "cn", value: "china", label: "China", continent: "Asia" },
16
{ code: "co", value: "colombia", label: "Colombia", continent: "South America" },
17
{ code: "eg", value: "egypt", label: "Egypt", continent: "Africa" },
18
{ code: "fr", value: "france", label: "France", continent: "Europe" },
19
{ code: "de", value: "germany", label: "Germany", continent: "Europe" },
20
{ code: "it", value: "italy", label: "Italy", continent: "Europe" },
21
{ code: "jp", value: "japan", label: "Japan", continent: "Asia" },
22
{ code: "ke", value: "kenya", label: "Kenya", continent: "Africa" },
23
{ code: "mx", value: "mexico", label: "Mexico", continent: "North America" },
24
{ code: "nz", value: "new-zealand", label: "New Zealand", continent: "Oceania" },
25
{ code: "ng", value: "nigeria", label: "Nigeria", continent: "Africa" },
26
{ code: "za", value: "south-africa", label: "South Africa", continent: "Africa" },
27
{ code: "kr", value: "south-korea", label: "South Korea", continent: "Asia" },
28
{ code: "gb", value: "united-kingdom", label: "United Kingdom", continent: "Europe" },
29
{ code: "us", value: "united-states", label: "United States", continent: "North America" },
30
];
31
32
export default function ComboboxCustom() {
33
return (
34
<Combobox<(typeof countries)[number]>
35
options={countries}
36
optionValue="value"
37
optionTextValue="label"
38
placeholder="Search countries..."
39
itemComponent={(props) => (
40
<ComboboxItem item={props.item}>
41
<Item size="xs" class="p-0">
42
<ItemContent>
43
<ItemTitle class="whitespace-nowrap">{props.item.rawValue.label}</ItemTitle>
44
<ItemDescription>
45
{props.item.rawValue.continent} ({props.item.rawValue.code})
46
</ItemDescription>
47
</ItemContent>
48
</Item>
49
</ComboboxItem>
50
)}
51
>
52
<ComboboxInput placeholder="Search countries..." />
53
<ComboboxContent>
54
<ComboboxEmpty>No countries found.</ComboboxEmpty>
55
</ComboboxContent>
56
</Combobox>
57
);
58
}

Invalid

Use the validationState="invalid" prop to make the combobox invalid.

1
import {
2
Combobox,
3
ComboboxContent,
4
ComboboxEmpty,
5
ComboboxInput,
6
ComboboxItem,
7
} from "~/components/ui/combobox";
8
9
const frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"];
10
11
export default function ComboboxInvalid() {
12
return (
13
<Combobox
14
options={frameworks}
15
placeholder="Select a framework..."
16
validationState="invalid"
17
itemComponent={(props) => (
18
<ComboboxItem item={props.item}>{props.item.rawValue}</ComboboxItem>
19
)}
20
>
21
<ComboboxInput placeholder="Select a framework..." aria-invalid="true" />
22
<ComboboxContent>
23
<ComboboxEmpty>No frameworks found.</ComboboxEmpty>
24
</ComboboxContent>
25
</Combobox>
26
);
27
}

Disabled

Use the disabled prop to disable the combobox.

1
import {
2
Combobox,
3
ComboboxContent,
4
ComboboxEmpty,
5
ComboboxInput,
6
ComboboxItem,
7
} from "~/components/ui/combobox";
8
9
const frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"];
10
11
export default function ComboboxDisabled() {
12
return (
13
<Combobox
14
options={frameworks}
15
placeholder="Select a framework..."
16
disabled
17
itemComponent={(props) => (
18
<ComboboxItem item={props.item}>{props.item.rawValue}</ComboboxItem>
19
)}
20
>
21
<ComboboxInput placeholder="Select a framework..." disabled />
22
<ComboboxContent>
23
<ComboboxEmpty>No frameworks found.</ComboboxEmpty>
24
</ComboboxContent>
25
</Combobox>
26
);
27
}

Popup

Use ComboboxTrigger to open a combobox from a button instead of a text input.

1
import { createSignal } from "solid-js";
2
import { Button } from "~/components/ui/button";
3
import {
4
Combobox,
5
ComboboxContent,
6
ComboboxEmpty,
7
ComboboxItem,
8
ComboboxTrigger,
9
} from "~/components/ui/combobox";
10
11
const countries = [
12
{ code: "ar", value: "argentina", label: "Argentina" },
13
{ code: "au", value: "australia", label: "Australia" },
14
{ code: "br", value: "brazil", label: "Brazil" },
15
{ code: "ca", value: "canada", label: "Canada" },
16
{ code: "cn", value: "china", label: "China" },
17
{ code: "fr", value: "france", label: "France" },
18
{ code: "de", value: "germany", label: "Germany" },
19
{ code: "jp", value: "japan", label: "Japan" },
20
{ code: "gb", value: "united-kingdom", label: "United Kingdom" },
21
{ code: "us", value: "united-states", label: "United States" },
22
];
23
24
export default function ComboboxPopup() {
25
const [value, setValue] = createSignal<(typeof countries)[number] | null>(null);
26
27
return (
28
<Combobox<(typeof countries)[number]>
29
options={countries}
30
value={value()}
31
onChange={setValue}
32
optionValue="value"
33
optionTextValue="label"
34
placeholder="Select country"
35
itemComponent={(props) => (
36
<ComboboxItem item={props.item}>{props.item.rawValue.label}</ComboboxItem>
37
)}
38
>
39
<ComboboxTrigger as={Button} variant="outline" class="w-64 justify-between font-normal">
40
{value()?.label ?? "Select country"}
41
</ComboboxTrigger>
42
<ComboboxContent>
43
<ComboboxEmpty>No countries found.</ComboboxEmpty>
44
</ComboboxContent>
45
</Combobox>
46
);
47
}

Input Group

Add an addon to the combobox by placing InputGroupAddon inside ComboboxInput.

1
import { GlobeIcon } from "lucide-solid";
2
import { Show } from "solid-js";
3
import {
4
Combobox,
5
ComboboxContent,
6
ComboboxEmpty,
7
ComboboxInput,
8
ComboboxItem,
9
ComboboxSection,
10
ComboboxSectionLabel,
11
ComboboxSeparator,
12
} from "~/components/ui/combobox";
13
import { InputGroupAddon } from "~/components/ui/input-group";
14
15
type TimezoneGroup = {
16
label: string;
17
options: string[];
18
};
19
20
const timezones: TimezoneGroup[] = [
21
{
22
label: "Americas",
23
options: [
24
"(GMT-5) New York",
25
"(GMT-8) Los Angeles",
26
"(GMT-6) Chicago",
27
"(GMT-5) Toronto",
28
"(GMT-8) Vancouver",
29
"(GMT-3) São Paulo",
30
],
31
},
32
{
33
label: "Europe",
34
options: [
35
"(GMT+0) London",
36
"(GMT+1) Paris",
37
"(GMT+1) Berlin",
38
"(GMT+1) Rome",
39
"(GMT+1) Madrid",
40
"(GMT+1) Amsterdam",
41
],
42
},
43
{
44
label: "Asia/Pacific",
45
options: [
46
"(GMT+9) Tokyo",
47
"(GMT+8) Shanghai",
48
"(GMT+8) Singapore",
49
"(GMT+4) Dubai",
50
"(GMT+11) Sydney",
51
"(GMT+9) Seoul",
52
],
53
},
54
];
55
56
export default function ComboboxInputGroup() {
57
return (
58
<Combobox<string, TimezoneGroup>
59
options={timezones}
60
optionValue={(option) => option}
61
optionTextValue={(option) => option}
62
optionGroupChildren="options"
63
placeholder="Select a timezone..."
64
itemComponent={(props) => (
65
<ComboboxItem item={props.item}>{props.item.rawValue}</ComboboxItem>
66
)}
67
sectionComponent={(props) => (
68
<>
69
<Show when={props.section.index !== 0}>
70
<ComboboxSeparator />
71
</Show>
72
<ComboboxSection>
73
<ComboboxSectionLabel>{props.section.rawValue.label}</ComboboxSectionLabel>
74
</ComboboxSection>
75
</>
76
)}
77
>
78
<ComboboxInput placeholder="Select a timezone...">
79
<InputGroupAddon>
80
<GlobeIcon class="size-4" />
81
</InputGroupAddon>
82
</ComboboxInput>
83
<ComboboxContent class="w-60">
84
<ComboboxEmpty>No timezones found.</ComboboxEmpty>
85
</ComboboxContent>
86
</Combobox>
87
);
88
}

API Reference

See the Kobalte Combobox API reference for primitive props, keyboard behavior, and accessibility details.

On This Page

  • Installation
  • Usage
  • Composition
    • Simple
    • With Chips
    • With Groups
  • Custom Items
  • Multiple Selection
  • Basic
  • Multiple
  • Clear Button
  • Groups
  • Custom Items
  • Invalid
  • Disabled
  • Popup
  • Input Group
  • API Reference
Built by Kevin Abatan. The source code is available on GitHub.