import { Combobox } from "@elabs-ai/components-ui";npm install @elabs-ai/components-tokens @elabs-ai/components-uiPrompt for your coding agent
Paste it into Claude Code, Cursor, Codex or any other coding agent. Text in [brackets] is yours to fill in.
Add the brand-ui Combobox component (from @elabs-ai/components-ui) to this project. 1. Read https://elabs-ai.com/llms.txt first: it maps every package and the rules. If you can use MCP servers, connect https://elabs-ai.com/mcp and use its search, docs and tokens tools. If not, run the same lookups in the terminal: `npx -y @elabs-ai/components-cli search "<what you need>"` and `npx -y @elabs-ai/components-cli docs <Component>`. 2. Look Combobox up before writing code: `npx -y @elabs-ai/components-cli docs Combobox` prints its real props, variants, what it pairs with and what to avoid. 3. If @elabs-ai/components-ui is not installed yet: `pnpm add @elabs-ai/components-tokens @elabs-ai/components-ui` (or `npm install @elabs-ai/components-tokens @elabs-ai/components-ui`). If this is the first brand-ui package in the project, wire it once: import `@elabs-ai/components-tokens/styles.css` plus the `themes/light.css` and `themes/dark.css` stylesheets in the CSS entry, add one Tailwind `@source` line per installed @elabs-ai package (Tailwind does not scan node_modules, so without it everything renders unstyled), and wrap the app root in `ThemeProvider` from `@elabs-ai/components-tokens`. 4. Use it here: [the screen or file, and what it should show] 5. Rules: use only @elabs-ai/components-* components and the semantic tokens — no raw colours, no raw font sizes, no arbitrary Tailwind values. Look up each component's real props with the docs tool before you use it; never guess a prop. 6. Done means: the typecheck passes, `npx -y @elabs-ai/components-cli audit src --strict` reports nothing, and you have opened the app and looked at every screen you touched in the light and the dark theme.
Avoid
- Using Combobox for a tiny fixed option set — a plain Select is simpler and lighter.
- Restyling the trigger/panel by hand — Combobox is a Popover + Command assembly; style through those parts.
Works with
Examples
Disabled
Enabled vs disabled, side by side (#343).
Accessible Name
`aria-label` names the control's PURPOSE ("Session switcher"), independent of the selected value's own text (#347).
Allow custom value
`allowCustomValue` accepts free text alongside the suggested options — a value not in `options` is offered via a "Use…" suggestion, still filtered live as the user types (#359).
API
Combobox
| Prop | Type | Description |
|---|---|---|
optionsrequired | ComboboxOption[] | The option list — every option is mounted in the DOM (cmdk has no built-in windowing), so this is fine for typical form/menu-sized lists but not for open-ended data. For thousands of options, use `VirtualSelect` instead, which windows rendering via `@tanstack/react-virtual`. |
value | string | |
onValueChange | (value: string) => void | |
placeholder | string | |
searchPlaceholder | string | |
emptyText | string | Message shown when the search yields no results. Defaults to "No results." — or, when `allowCustomValue` is set, a message that also signals a custom value is acceptable. |
className | string | |
disabled | boolean | Disables the trigger and prevents the popover from opening. |
allowCustomValue | boolean | When `true`, typed text that doesn't match any option becomes a submittable value: a synthetic "Use…" suggestion is shown, and pressing Enter with no listed option highlighted accepts the typed text via `onValueChange`. Suggestions still filter as usual. Default `false` (selection-only, unchanged behavior) — #359. |
id | string | `id` applied to the trigger button. |
triggerProps | Omit< ComponentPropsWithoutRef<typeof Button>, "role" | "aria-expanded" | "disabled" > & { /** Arbitrary `data-*` attributes (test hooks, analytics, …). */ [key: `data-${string}`]: string | number | boolean | undefined; } | Escape hatch spread onto the trigger `Button` (after the named props above, so it wins if both are set) for anything not covered by a named prop — `data-*`, `name`, `data-testid`, etc. `role`/`aria-expanded` stay component-owned and can't be passed here; disabling stays on the top-level `disabled` prop so the popover-open guard below can't drift out of sync with the trigger's actual disabled state. |
Theming
The tokens each state resolves to. Change the token and every theme follows.
| State | Token |
|---|---|
| focus | focus-ring — the compound indicator: a ring-2 in --ring plus a 1px --ring-contour outline (#67) — inherited: the trigger is a Button |
| highlighted | data-[selected=true]:bg-accent + text-accent-foreground — inherited from CommandItem |