Combobox
Filterable select for choosing from a predefined list of items.
"use client";
import { useRef } from "react";
import {
Combobox,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
ComboboxPopup,
ComboboxPortal,
ComboboxPositioner,
ComboboxTrigger,
} from "@/components/ui/combobox/combobox";
import styles from "./combobox-demo.module.css";
type Country = {
code: string;
name: string;
flag: string;
};
const countries: Country[] = [
{ code: "AU", name: "Australia", flag: "🇦🇺" },
{ code: "BR", name: "Brazil", flag: "🇧🇷" },
{ code: "CA", name: "Canada", flag: "🇨🇦" },
{ code: "CN", name: "China", flag: "🇨🇳" },
{ code: "DK", name: "Denmark", flag: "🇩🇰" },
{ code: "FI", name: "Finland", flag: "🇫🇮" },
{ code: "FR", name: "France", flag: "🇫🇷" },
{ code: "DE", name: "Germany", flag: "🇩🇪" },
{ code: "IN", name: "India", flag: "🇮🇳" },
{ code: "IT", name: "Italy", flag: "🇮🇹" },
{ code: "JP", name: "Japan", flag: "🇯🇵" },
{ code: "MX", name: "Mexico", flag: "🇲🇽" },
{ code: "NL", name: "Netherlands", flag: "🇳🇱" },
{ code: "NO", name: "Norway", flag: "🇳🇴" },
{ code: "PL", name: "Poland", flag: "🇵🇱" },
{ code: "ES", name: "Spain", flag: "🇪🇸" },
{ code: "SE", name: "Sweden", flag: "🇸🇪" },
{ code: "CH", name: "Switzerland", flag: "🇨ðŸ‡" },
{ code: "GB", name: "United Kingdom", flag: "🇬🇧" },
{ code: "US", name: "United States", flag: "🇺🇸" },
];
export default function ComboboxDemo() {
const anchorRef = useRef<HTMLDivElement>(null);
return (
<div className={styles.container}>
<label className={styles.label} htmlFor="cb-input">
Select your country
</label>
<div className={styles.comboboxWrapper}>
<Combobox<Country>
items={countries}
itemToStringLabel={(item) => item?.name || ""}
itemToStringValue={(item) => item?.code || ""}
>
<div className={styles.inputWrapper} ref={anchorRef}>
<ComboboxInput id="cb-input" placeholder="Search countries..." />
<ComboboxTrigger />
</div>
<ComboboxPortal>
<ComboboxPositioner anchor={anchorRef}>
<ComboboxPopup className={styles.popup}>
<ComboboxEmpty>No country found.</ComboboxEmpty>
<ComboboxList>
{(country: Country) => (
<ComboboxItem indicatorPosition="right" key={country.code} value={country}>
<div className={styles.countryContainer}>
<span className={styles.countryFlag}>{country.flag}</span>
<span className={styles.countryName}>{country.name}</span>
</div>
</ComboboxItem>
)}
</ComboboxList>
</ComboboxPopup>
</ComboboxPositioner>
</ComboboxPortal>
</Combobox>
</div>
</div>
);
}
npx shadcn@latest add @roiui/comboboxnpx shadcn@latest add @roiui/combobox-tailwindanatomy
<Combobox>
<ComboboxInput />
<ComboboxTrigger />
<ComboboxPortal>
<ComboboxPositioner>
<ComboboxPopup>
<ComboboxList>
<ComboboxItem>
<ComboboxItemText />
<ComboboxItemIndicator />
</ComboboxItem>
</ComboboxList>
</ComboboxPopup>
</ComboboxPositioner>
</ComboboxPortal>
</Combobox>"use client";
import { useRef } from "react";
import {
Combobox,
ComboboxClear,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
ComboboxPopup,
ComboboxPortal,
ComboboxPositioner,
ComboboxTrigger,
ComboboxValue,
} from "@/components/ui/combobox/combobox";
import styles from "./combobox-clearable.module.css";
const frameworks = [
{ value: "next", label: "Next.js" },
{ value: "remix", label: "Remix" },
{ value: "astro", label: "Astro" },
{ value: "nuxt", label: "Nuxt" },
{ value: "sveltekit", label: "SvelteKit" },
{ value: "gatsby", label: "Gatsby" },
];
type Framework = (typeof frameworks)[0];
export default function ComboboxClearable() {
const anchorRef = useRef<HTMLDivElement>(null);
return (
<div className={styles.container}>
<label className={styles.label} htmlFor="clearable-input">
Select a framework
</label>
<div className={styles.comboboxWrapper}>
<Combobox<Framework>
items={frameworks}
itemToStringLabel={(item) => item?.label || ""}
itemToStringValue={(item) => item?.value || ""}
>
<div className={styles.inputWrapper} ref={anchorRef}>
<ComboboxInput id="clearable-input" placeholder="Search frameworks..." />
<ComboboxValue>
{(value: Framework | null) => (value ? <ComboboxClear /> : <ComboboxTrigger />)}
</ComboboxValue>
</div>
<ComboboxPortal>
<ComboboxPositioner anchor={anchorRef}>
<ComboboxPopup className={styles.popup}>
<ComboboxEmpty>No frameworks found.</ComboboxEmpty>
<ComboboxList>
{(item: Framework) => (
<ComboboxItem indicatorPosition="right" key={item.value} value={item}>
<span style={{ flex: 1 }}>{item.label}</span>
</ComboboxItem>
)}
</ComboboxList>
</ComboboxPopup>
</ComboboxPositioner>
</ComboboxPortal>
</Combobox>
</div>
</div>
);
}
"use client";
import { useRef } from "react";
import {
Combobox,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
ComboboxPopup,
ComboboxPortal,
ComboboxPositioner,
ComboboxTrigger,
} from "@/components/ui/combobox/combobox";
import styles from "./combobox-auto-highlight.module.css";
const countries = [
{ value: "us", label: "United States" },
{ value: "uk", label: "United Kingdom" },
{ value: "ca", label: "Canada" },
{ value: "au", label: "Australia" },
{ value: "de", label: "Germany" },
{ value: "fr", label: "France" },
{ value: "jp", label: "Japan" },
{ value: "br", label: "Brazil" },
];
type Country = (typeof countries)[0];
export default function ComboboxAutoHighlight() {
const anchorRef = useRef<HTMLDivElement>(null);
return (
<div className={styles.container}>
<label className={styles.label} htmlFor="auto-highlight-input">
Select a country
</label>
<div className={styles.comboboxWrapper}>
<Combobox<Country>
autoHighlight
items={countries}
itemToStringLabel={(item) => item?.label || ""}
itemToStringValue={(item) => item?.value || ""}
>
<div className={styles.inputWrapper} ref={anchorRef}>
<ComboboxInput id="auto-highlight-input" placeholder="Start typing..." />
<ComboboxTrigger />
</div>
<ComboboxPortal>
<ComboboxPositioner anchor={anchorRef}>
<ComboboxPopup className={styles.popup}>
<ComboboxEmpty>No countries found.</ComboboxEmpty>
<ComboboxList>
{(item: Country) => (
<ComboboxItem indicatorPosition="right" key={item.value} value={item}>
<span style={{ flex: 1 }}>{item.label}</span>
</ComboboxItem>
)}
</ComboboxList>
</ComboboxPopup>
</ComboboxPositioner>
</ComboboxPortal>
</Combobox>
</div>
</div>
);
}
"use client";
import { useRef } from "react";
import {
Combobox,
ComboboxEmpty,
ComboboxGroup,
ComboboxGroupLabel,
ComboboxInput,
ComboboxItem,
ComboboxList,
ComboboxPopup,
ComboboxPortal,
ComboboxPositioner,
ComboboxTrigger,
} from "@/components/ui/combobox/combobox";
import styles from "./combobox-grouped.module.css";
type Item = {
value: string;
label: string;
};
type Group = {
label: string;
items: Item[];
};
const groups: Group[] = [
{
label: "Fruits",
items: [
{ value: "apple", label: "Apple" },
{ value: "banana", label: "Banana" },
{ value: "orange", label: "Orange" },
],
},
{
label: "Vegetables",
items: [
{ value: "carrot", label: "Carrot" },
{ value: "broccoli", label: "Broccoli" },
{ value: "spinach", label: "Spinach" },
],
},
{
label: "Dairy",
items: [
{ value: "milk", label: "Milk" },
{ value: "cheese", label: "Cheese" },
{ value: "yogurt", label: "Yogurt" },
],
},
];
export default function ComboboxGrouped() {
const anchorRef = useRef<HTMLDivElement>(null);
return (
<div className={styles.container}>
<label className={styles.label} htmlFor="grouped-input">
Select an item
</label>
<div className={styles.comboboxWrapper}>
<Combobox<Group>
items={groups}
itemToStringLabel={(item) => (item as unknown as Item)?.label || ""}
itemToStringValue={(item) => (item as unknown as Item)?.value || ""}
>
<div className={styles.inputWrapper} ref={anchorRef}>
<ComboboxInput id="grouped-input" placeholder="Search items..." />
<ComboboxTrigger />
</div>
<ComboboxPortal>
<ComboboxPositioner anchor={anchorRef}>
<ComboboxPopup className={styles.popup}>
<ComboboxEmpty>No items found.</ComboboxEmpty>
<ComboboxList>
{(group: Group) => (
<ComboboxGroup key={group.label}>
<ComboboxGroupLabel>{group.label}</ComboboxGroupLabel>
{group.items.map((item) => (
<ComboboxItem indicatorPosition="right" key={item.value} value={item}>
<span style={{ flex: 1 }}>{item.label}</span>
</ComboboxItem>
))}
</ComboboxGroup>
)}
</ComboboxList>
</ComboboxPopup>
</ComboboxPositioner>
</ComboboxPortal>
</Combobox>
</div>
</div>
);
}
"use client";
import { useId, useRef } from "react";
import {
Combobox,
ComboboxChip,
ComboboxChipRemove,
ComboboxChips,
ComboboxChipsInput,
ComboboxEmpty,
ComboboxItem,
ComboboxList,
ComboboxPopup,
ComboboxPortal,
ComboboxPositioner,
ComboboxValue,
} from "@/components/ui/combobox/combobox";
import styles from "./combobox-multiple.module.css";
interface Language {
id: string;
value: string;
}
const languages: Language[] = [
{ id: "js", value: "JavaScript" },
{ id: "ts", value: "TypeScript" },
{ id: "py", value: "Python" },
{ id: "java", value: "Java" },
{ id: "cpp", value: "C++" },
{ id: "cs", value: "C#" },
{ id: "php", value: "PHP" },
{ id: "ruby", value: "Ruby" },
{ id: "go", value: "Go" },
{ id: "rust", value: "Rust" },
{ id: "swift", value: "Swift" },
];
export default function ComboboxMultiple() {
const containerRef = useRef<HTMLDivElement>(null);
const id = useId();
return (
<div className={styles.container}>
<label className={styles.label} htmlFor={id}>
Programming languages
</label>
<div className={styles.comboboxWrapper}>
<Combobox<Language, true> items={languages} multiple>
<ComboboxChips ref={containerRef}>
<ComboboxValue>
{(value: Language[]) => (
<>
{value.map((language) => (
<ComboboxChip aria-label={language.value} key={language.id}>
{language.value}
<ComboboxChipRemove aria-label="Remove" />
</ComboboxChip>
))}
<ComboboxChipsInput id={id} placeholder={value.length > 0 ? "" : "e.g. TypeScript"} />
</>
)}
</ComboboxValue>
</ComboboxChips>
<ComboboxPortal>
<ComboboxPositioner anchor={containerRef}>
<ComboboxPopup className={styles.popup}>
<ComboboxEmpty>No languages found.</ComboboxEmpty>
<ComboboxList>
{(language: Language) => (
<ComboboxItem indicatorPosition="right" key={language.id} value={language}>
<span style={{ flex: 1 }}>{language.value}</span>
</ComboboxItem>
)}
</ComboboxList>
</ComboboxPopup>
</ComboboxPositioner>
</ComboboxPortal>
</Combobox>
</div>
</div>
);
}