Github

Combobox

Autocomplete input with a list of suggestions.

"use client";
import {
	Combobox,
	ComboboxContent,
	ComboboxEmpty,
	ComboboxInput,
	ComboboxItem,
	ComboboxList,
} from "@/components/ui/combobox";

const frameworks = [
	"Next.js",
	"SvelteKit",
	"Nuxt.js",
	"Remix",
	"Astro",
] as const;

export function ComboboxDemo() {
	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 shadcn@latest add https://herocn.dev/r/combobox.json

Usage

import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxInput,
  ComboboxItem,
  ComboboxList,
} from "@/components/ui/combobox"

Pass an items array to Combobox and render items inside ComboboxList using the render prop.

const frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"]

<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

Use the following composition to build a Combobox:

Simple

Combobox
├── ComboboxInput
└── ComboboxContent
    ├── ComboboxEmpty
    └── ComboboxList
        ├── ComboboxItem
        └── ComboboxItem

With chips

Multi-select with multiple, chips, and a chips input.

Combobox
├── ComboboxChips
│   ├── ComboboxValue
│   │   └── ComboboxChip
│   └── ComboboxChipsInput
└── ComboboxContent
    ├── ComboboxEmpty
    └── ComboboxList
        ├── ComboboxItem
        └── ComboboxItem

With groups

Nested items per group using ComboboxCollection inside each ComboboxGroup, with a separator between groups.

Combobox
├── ComboboxInput
└── ComboboxContent
    ├── ComboboxEmpty
    └── ComboboxList
        ├── ComboboxGroup
        │   ├── ComboboxLabel
        │   └── ComboboxCollection
        │       ├── ComboboxItem
        │       └── ComboboxItem
        ├── ComboboxSeparator
        └── ComboboxGroup
            ├── ComboboxLabel
            └── ComboboxCollection
                ├── ComboboxItem
                └── ComboboxItem

Examples

Multiple

Use multiple, ComboboxChips, ComboboxChip, and ComboboxChipsInput for multi-select.

"use client";

import * as React from "react";

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} className="w-full max-w-xs">
				<ComboboxValue>
					{(values) => (
						<React.Fragment>
							{values.map((value: string) => (
								<ComboboxChip key={value}>{value}</ComboboxChip>
							))}
							<ComboboxChipsInput />
						</React.Fragment>
					)}
				</ComboboxValue>
			</ComboboxChips>
			<ComboboxContent anchor={anchor}>
				<ComboboxEmpty>No items found.</ComboboxEmpty>
				<ComboboxList>
					{(item) => (
						<ComboboxItem key={item} value={item}>
							{item}
						</ComboboxItem>
					)}
				</ComboboxList>
			</ComboboxContent>
		</Combobox>
	);
}

Groups

Use ComboboxGroup, ComboboxLabel, ComboboxCollection, and ComboboxSeparator to organize items.

"use client";
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 ComboboxGroups() {
	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>
	);
}

Clear Button

Set showClear on ComboboxInput to show a clear button.

"use client";

import {
	Combobox,
	ComboboxContent,
	ComboboxEmpty,
	ComboboxInput,
	ComboboxItem,
	ComboboxList,
} from "@/components/ui/combobox";

const frameworks = [
	"Next.js",
	"SvelteKit",
	"Nuxt.js",
	"Remix",
	"Astro",
] as const;

export function ComboboxClear() {
	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>
	);
}

Disabled

Set disabled on ComboboxInput to disable the control.

"use client";
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>
	);
}

Invalid

Set aria-invalid="true" on ComboboxInput to show an error state.

"use client";
import {
	Combobox,
	ComboboxContent,
	ComboboxEmpty,
	ComboboxInput,
	ComboboxItem,
	ComboboxList,
} from "@/components/ui/combobox";
import { Field, FieldError, FieldLabel } from "@/components/ui/field";

const frameworks = [
	"Next.js",
	"SvelteKit",
	"Nuxt.js",
	"Remix",
	"Astro",
] as const;

export function ComboboxInvalid() {
	return (
		<Field data-invalid className="w-full max-w-48">
			<FieldLabel>Framework</FieldLabel>
			<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>
			<FieldError>Please select a framework.</FieldError>
		</Field>
	);
}

Auto Highlight

Set autoHighlight on the root Combobox to automatically highlight the first item on filter.

"use client";

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>
	);
}

Use ComboboxTrigger and ComboboxValue to trigger the combobox from a button. Move ComboboxInput inside ComboboxContent.

"use client";

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="tertiary"
						className="w-64 justify-between font-normal"
					>
						<ComboboxValue />
					</Button>
				}
			/>
			<ComboboxContent className="min-w-(--anchor-width)">
				<ComboboxInput
					variant="secondary"
					showTrigger={false}
					placeholder="Search"
				/>
				<ComboboxEmpty>No items found.</ComboboxEmpty>
				<ComboboxList>
					{(item) => (
						<ComboboxItem key={item.code} value={item}>
							{item.label}
						</ComboboxItem>
					)}
				</ComboboxList>
			</ComboboxContent>
		</Combobox>
	);
}

Variants

The Combobox component supports two visual variants:

  • default (default) — Standard styling, suitable for most use cases
  • secondary — Lower emphasis variant, suitable for use in Surface components
"use client";
import {
	Combobox,
	ComboboxContent,
	ComboboxEmpty,
	ComboboxInput,
	ComboboxItem,
	ComboboxList,
} from "@/components/ui/combobox";

const frameworks = [
	"Next.js",
	"SvelteKit",
	"Nuxt.js",
	"Remix",
	"Astro",
] as const;

export function ComboboxVariants() {
	return (
		<div className="flex w-full max-w-sm flex-col gap-4">
			<Combobox items={frameworks}>
				<ComboboxInput placeholder="Default" />
				<ComboboxContent>
					<ComboboxEmpty>No items found.</ComboboxEmpty>
					<ComboboxList>
						{(item) => (
							<ComboboxItem key={item} value={item}>
								{item}
							</ComboboxItem>
						)}
					</ComboboxList>
				</ComboboxContent>
			</Combobox>
			<Combobox items={frameworks} variant="secondary">
				<ComboboxInput placeholder="Secondary" />
				<ComboboxContent>
					<ComboboxEmpty>No items found.</ComboboxEmpty>
					<ComboboxList>
						{(item) => (
							<ComboboxItem key={item} value={item}>
								{item}
							</ComboboxItem>
						)}
					</ComboboxList>
				</ComboboxContent>
			</Combobox>
		</div>
	);
}

In Surface

Place the combobox inside a Surface panel. Use variant="secondary" on Combobox so the input sits cleanly on the surface background.

Framework

Select your preferred framework.

"use client";
import {
	Combobox,
	ComboboxContent,
	ComboboxEmpty,
	ComboboxInput,
	ComboboxItem,
	ComboboxList,
} from "@/components/ui/combobox";
import { Surface } from "@/components/ui/surface";

const frameworks = ["Next.js", "SvelteKit", "Nuxt.js", "Remix", "Astro"];

export function ComboboxSurface() {
	return (
		<Surface className="w-full max-w-sm rounded-3xl border p-4">
			<h4 className="font-medium text-sm">Framework</h4>
			<p className="mt-1 text-muted-foreground text-sm">
				Select your preferred framework.
			</p>
			<div className="mt-4">
				<Combobox items={frameworks} variant="secondary">
					<ComboboxInput placeholder="Select a framework" />
					<ComboboxContent>
						<ComboboxEmpty>No items found.</ComboboxEmpty>
						<ComboboxList>
							{(item) => (
								<ComboboxItem key={item} value={item}>
									{item}
								</ComboboxItem>
							)}
						</ComboboxList>
					</ComboboxContent>
				</Combobox>
			</div>
		</Surface>
	);
}

RTL

To enable RTL support in shadcn/ui, see the RTL configuration guide.

"use client";

import * as React from "react";

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
			dir={dir}
			data-lang={dir === "rtl" ? language : undefined}
			className="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) => (
							<React.Fragment>
								{values.map((value: string) => (
									<ComboboxChip key={value}>
										{categoryLabels[value] || value}
									</ComboboxChip>
								))}
								<ComboboxChipsInput placeholder={t.placeholder} />
							</React.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>
	);
}

API Reference

The combobox is built on Base UI Combobox. Combobox forwards props to the root; sub-components forward to their corresponding Base UI primitives.

Combobox

PropTypeDefault

ComboboxInput

PropTypeDefault

ComboboxChip

PropTypeDefault

See the Base UI Combobox API reference for all other props (items, value, onValueChange, multiple, autoHighlight, positioning, etc.).