shadcn's combobox is built on Base UI, not Popover and Command

Searchable selects are often built as a Popover around a Command list. The shadcn combobox is not. Its source builds on Base UI's Combobox primitive, so the wiring looks different from the guides you may have read.
What the shadcn page says
The page describes the component as "Autocomplete input with a list of suggestions." The install command is pnpm dlx shadcn@latest add combobox, with tabs for npm, yarn and bun. The examples cover a basic framework list, multiple selection with chips, groups, a clear button, custom items, invalid and disabled states, auto highlight, an input group, a popup with a "Select country" trigger, and a right-to-left example.
The page does not say how the component is built. It does not mention Popover or Command. Those names appear only in the site's own navigation. So if you expect a Popover wrapped around a Command list, check the source before you copy steps from another guide.
What the source is built on
The combobox registry item imports Combobox from @base-ui/react and renames it ComboboxPrimitive. It also imports cn for class names, the chevron, check and clear icons from lucide-react, and two other registry items: the button and the input group. The file does not import from popover, command or cmdk.
This matters in two places. First, the parts you compose are named after the Base UI primitive, not after a Popover and a Command list. Second, the combobox file imports the input group parts, so treat the input as part of the combobox, not a plain input you can swap in without thought. Read the component file once after you install it, so you know which parts you own.
11 production screens. Login and database wired.
The SaaS Dashboard Kit includes login, a database, and a working dashboard. Live demo at saas.otf-kit.dev.
Wire a basic combobox
The page's basic example uses the same parts as its multiple selection example. You pass the list to the root with items, put a ComboboxInput above a ComboboxContent, and render each option inside a ComboboxList with a function that gets one item at a time. The empty state goes inside the content, so the user sees a message when the filter removes every option:
import {
Combobox,
ComboboxContent,
ComboboxEmpty,
ComboboxInput,
ComboboxItem,
ComboboxList,
} from "@/components/ui/combobox"
const countries = ["Canada", "Germany", "India", "Japan", "Brazil"]
export function CountryPicker() {
return (
<Combobox items={countries} autoHighlight>
<ComboboxInput placeholder="Select a country" />
<ComboboxContent>
<ComboboxEmpty>No countries found.</ComboboxEmpty>
<ComboboxList>
{(item) => (
<ComboboxItem key={item} value={item}>
{item}
</ComboboxItem>
)}
</ComboboxList>
</ComboboxContent>
</Combobox>
)
}The autoHighlight prop highlights the first matching option as the user types, so pressing Enter picks something sensible without an arrow key. Check that default against your data. A short list of countries may want it on. A list of users, where the first match may be the wrong person, may want it off.
The page's basic example is collapsed behind its View Code link, so compare this sketch with that code before you ship. The props and parts shown here are the ones the page's other examples use.

Add multiple selection
A combobox that picks several values keeps its selection as a list. The page's multiple selection example sets multiple on the root, stores an array of strings in React state, and passes both value and onValueChange to the root. The selected values show as chips, using ComboboxChip inside ComboboxChips, and a ComboboxChipsInput takes the typing where a single input would go.
The shape to keep in mind is simple. One state value holds the array, the root reads it, and the chips render from it. The example uses a five-name framework list, which is enough to see the chips and the filter work together.
Handle long lists
A long list is where the wiring matters most. Three choices decide how it feels.
First, the items. If your list is an array of objects, such as users with a name and an id, the page says to pass itemToStringValue so the component knows what text to match and show. Set it for those lists.
Second, the filter. The page does not describe how filtering works. It only says that autoHighlight picks the first match as the user types. Do not assume the filter does what you need for names with accents, initials or word order. Test it with your own list, and with the terms your users actually type.
Third, the count. Five countries will look fine with any choice. Five hundred rows will show whether the list re-renders slowly on each keystroke. Measure the first keystroke and the tenth before you decide the list is ready.
Test the keyboard and the empty state
Searchable selects fail for keyboard users first. Check these before you ship:
- Tab into the input, type a term, then use the arrow keys to move through the matches.
- Press Enter on a highlighted option. The value should set, and the list should close.
- Press Escape. The list should close without changing the value.
- Type a term with no match. The empty message should show, and the form should not submit a value the user did not choose.
- In multiple selection, remove a chip with the keyboard. Focus should move somewhere sensible, not to the top of the page.
Check the empty message's wording too. "No countries found." tells the user what they searched. A bare "No results" does not.

Choose a combobox over a plain select
A plain select suits a short, fixed list that users scan, such as a size or a billing period. The user can see every option without typing. A combobox suits a list where the user already knows what they want, such as a country, a team member or a product, and the list is too long to scan. Typing narrows it faster than scrolling.
The two also differ in what the user can do wrong. A plain select cannot hold a value that is not in its list. A combobox can show a term with no match, which is why the empty message and the form validation both matter. Decide on the list first. If it fits on one screen without scrolling, a select is usually the simpler choice, and you can skip the filter, the empty state and the highlight rules in this article.
Where OTF stands
If you want a searchable select already built into a product, we cannot point you to one. We checked the SaaS Dashboard, Booking, Fitness and Arcade kit pages for combobox, select and searchable-select terms, and none of them lists one. So this article does not say any OTF kit ships one. The pricing page lists the Full-stack kits at $99 each and the Everything Bundle at $149, both as one-time payments, and it offers a live demo before you buy. Check each kit's repository for the components it ships before you rely on any of them.
The date picker guide covers the other control that often sits in a form beside a select, and the data table guide covers the sortable table. If you are choosing a starting point for a product, the pricing page is the place to compare kits side by side: OTF pricing.
Sources
- shadcn/ui, Combobox: https://ui.shadcn.com/docs/components/combobox
- OTF pricing: otf-kit.dev/pricing
Ship the product, not the setup.
- 11 production screens — auth, team, analytics, and settings
- Login and a real database are wired in
- AI configs pre-tuned so your agent extends instead of regenerates