Combobox
An input component that combines a text field with a dropdown list, allowing users to either type a value directly or select one from a predefined, searchable list of options. Ideal for autocomplete scenarios or command palettes.
Component Installation and Dependencies
The Combobox component is not a standalone primitive but rather a sophisticated composition leveraging two other core components:
<Popover />: Used to display the dropdown list of suggestions.<Command />: Provides the searching/filtering capabilities within the popover list.
Therefore, to use the Combobox, you must first ensure that both the Popover and the Command components are installed and available in your project. Follow the installation instructions provided on their respective documentation pages.
Implementing the Combobox
"use client"; // Directive for Next.js App Router, indicating client-side interactivity
Command, // Core command component for search functionality
CommandEmpty, // Displayed when no results are found
CommandGroup, // Groups command items
CommandInput, // The search input field
CommandItem, // Represents a single selectable item
CommandList, // Container for command items, handles scrolling
} from "@/components/ui/command";
import {
Popover, // Container for the dropdown content
PopoverContent, // The actual dropdown panel
PopoverTrigger, // The element that opens the popover (our button)
} from "@/components/ui/popover";
// Sample data structure for the combobox options
const frameworks = [
{ value: "next.js", label: "Next.js" },
{ value: "sveltekit", label: "SvelteKit" },
{ value: "nuxt.js", label: "Nuxt.js" },
{ value: "remix", label: "Remix" },
{ value: "astro", label: "Astro" },
];
export function ComboboxDemo() {
// State to manage the popover's open/closed status
const [open, setOpen] = React.useState(false);
// State to store the selected value
const [value, setValue] = React.useState("");
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox" // ARIA role for accessibility
aria-expanded={open} // ARIA state indicating if dropdown is open
className="w-[200px] justify-between" // Styling
>
{value
? frameworks.find((framework) => framework.value === value)?.label
: "Select framework..."}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-[200px] p-0">
<Command>
<CommandInput placeholder="Search framework..." />
<CommandList>
<CommandEmpty>No framework found.</CommandEmpty>
<CommandGroup>
{frameworks.map((framework) => (
<CommandItem
key={framework.value}
value={framework.value} // Value used for matching and selection
// Handler for when an item is selected
onSelect={(currentValue) => {
setValue(currentValue === value ? "" : currentValue);
setOpen(false);
}}
>
<Check
className={cn(
"mr-2 h-4 w-4",
value === framework.value ? "opacity-100" : "opacity-0",
)}
/>
{framework.label}
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
);
}The core idea is to use a <Button> wrapped in a <PopoverTrigger> to open a <PopoverContent>. Inside the content, a <Command> component handles the list rendering, searching (<CommandInput>), and item selection (<CommandItem>). State (open, value) is managed using React.useState to control the popover visibility and track the selected item.
Combobox Variations and Examples
Standard Combobox Implementation
Alternative Trigger: Popover Integration
Integration within a Dropdown Menu
Responsive Design Adaptation
Achieve a responsive user experience by conditionally rendering the dropdown. Use the standard <Popover /> for wider screens (desktop) and switch to a <Drawer /> component for narrower screens (mobile), providing a more touch-friendly interface on smaller devices.