A text field that filters a list of options as you type and picks one, with optional create.
Combobox is the editable combobox: typing opens a frosted list and narrows it by label, description and keywords. It suits long lists people know the name of (a project, a country, a customer). For a short fixed list a person compares, use Select; Select's searchable mode is the same idea with a button trigger.
Leaving the field restores the chosen option's label, so the text never disagrees with the value. creatable offers "Create …" when the text matches nothing, and hands the text to onCreate.
inline renders the list open in normal flow under the field instead of a portalled popover: for docs, previews and pickers that live inside a panel.
Installation
npx shadcn@latest add KezLahd/rime/comboboxAlso installs: base, field, icon, text-input.
Usage
import { Combobox } from "@/components/ui";
<Field label="Project">
<Combobox options={PROJECTS} value={project} onChange={setProject} placeholder="Type to search projects" />
</Field>Examples
Open list
- Atlas redesignDesign · 12 tasks
- Beacon launchMarketing · 8 tasks
- Cobalt APIEngineering · 21 tasks
- Delta onboardingCustomer success · 5 tasks
- Ember billingFinance · 9 tasks
In a Field
Creatable
Type a new tag to create it.
API
Generated from the component's TypeScript types and JSDoc (scripts/gen-ui-registry.mjs), so it always matches the source.
Combobox
| Prop | Type | Default | Description |
|---|---|---|---|
| optionsRequired | ReadonlyArray<ComboboxOption> | ||
| valueRequired | string | null | ||
| onChangeRequired | (value: string | null) => void | The chosen option's value, or null when the text is cleared. | |
| placeholder | string | ||
| creatable | boolean | Offer "Create …" when the typed text matches no option. onCreate receives the text; add the option and select it there. | |
| onCreate | (label: string) => void | ||
| emptyText | string | "No matches" | Shown when nothing matches and creatable is off. |
| size | "sm" | "md" | "md" | |
| disabled | boolean | ||
| invalid | boolean | ||
| id | string | ||
| name | string | ||
| aria-label | string | ||
| defaultOpen | boolean | false | Open on mount. |
| inline | boolean | false | Render the list in normal flow under the field instead of portalling a popover, and keep it open. For docs, previews and pickers inside a panel. |
| className | string |
Theme tokens
Per-component tokens are undeclared by default, so the default shown is the fallback the CSS uses. Set one at :root (or in theme.css) to restyle every instance. Live values are read from this page, so they follow the Rime Studio.
| Token | Default | Live value |
|---|---|---|
| --field-focus-border | rgba(var(--rgb-brand), 0.7) / var(--focus-color) | … |
| --field-focus-ring | var(--focus-halo) | … |
| --field-focus-ring-invalid | 0 0 0 4px rgba(var(--rgb-danger), 0.14) | … |
| --field-hover-border | rgba(var(--rgb-brand-deep), 0.3) | … |
| --input-bg | var(--surface-solid) | … |
| --input-border | var(--line-input) | … |
| --input-border-width | var(--border-w-field) | … |
| --input-font-size | var(--text-input) | … |
| --input-height-md | var(--field-h-md) | … |
| --input-height-sm | var(--field-h-sm) | … |
| --input-pad-x | calc(14px * var(--density)) | … |
| --input-radius | var(--r-md) / var(--r-sm) | … |
| --input-shadow | var(--shadow-input) | … |
| --popover-item-active-bg | linear-gradient(to right, rgba(var(--rgb-brand), 0.14), rgba(var(--rgb-brand-deep), 0.07)) | … |
| --popover-item-radius | var(--r-xs) | … |
| --popover-pad | calc(6px * var(--density)) | … |
| --popover-radius | var(--r-lg) | … |
System tokens it reads
| Token | Default | Live value |
|---|---|---|
| --border-w | tokens.css | … |
| --border-w-field | tokens.css | … |
| --brand | tokens.css | … |
| --danger-base | tokens.css | … |
| --danger-field-fill | tokens.css | … |
| --density | tokens.css | … |
| --dur | tokens.css | … |
| --dur-fast | tokens.css | … |
| --ease | tokens.css | … |
| --ease-out | tokens.css | … |
| --field-h-md | tokens.css | … |
| --field-h-sm | tokens.css | … |
| --focus-color | tokens.css | … |
| --focus-halo | tokens.css | … |
| --font-body | tokens.css | … |
| --font-mono | tokens.css | … |
| --ink-body | tokens.css | … |
| --ink-brand | tokens.css | … |
| --ink-disabled | tokens.css | … |
| --ink-muted | tokens.css | … |
| --ink-placeholder | tokens.css | … |
| --ink-secondary | tokens.css | … |
| --line-input | tokens.css | … |
| --line-soft | tokens.css | … |
| --popover-blur | tokens.css | … |
| --popover-border | tokens.css | … |
| --popover-shadow | tokens.css | … |
| --popover-surface | tokens.css | … |
| --r-lg | tokens.css | … |
| --r-md | tokens.css | … |
| --r-sm | tokens.css | … |
| --r-xs | tokens.css | … |
| --rgb-brand | tokens.css | … |
| --rgb-brand-deep | tokens.css | … |
| --rgb-danger | tokens.css | … |
| --scrollbar-thumb | tokens.css | … |
| --scrollbar-track | tokens.css | … |
| --scrollbar-width | tokens.css | … |
| --shadow-input | tokens.css | … |
| --surface-solid | tokens.css | … |
| --surface-sunken | tokens.css | … |
| --text-body | tokens.css | … |
| --text-caption | tokens.css | … |
| --text-input | tokens.css | … |
| --text-small | tokens.css | … |
| --wash-subtle | tokens.css | … |
| --z-popover | tokens.css | … |
Accessibility
- The input is role="combobox" with aria-expanded, aria-controls, aria-autocomplete="list" and aria-activedescendant, so focus stays in the field while Up and Down move the highlight.
- Keyboard: typing filters, Up and Down move (and open), Home and End jump, Enter picks, Esc closes and then clears, Tab closes and moves on.
- The number of results is announced through a polite status region as you type.
- Give it a Field label, or aria-label when it stands alone.
Do and don't
Do
- Search descriptions and keywords, not only labels.
- Use creatable only where adding a value is safe and expected (tags).
Don't
- Don't use Combobox for fewer than about eight options; use Select or RadioGroup.
- Don't allow free text that is not saved as an option; that is a TextInput.
For AI agents
This page as markdown, rendered from the same registry entry. Point an agent at it (or add .md to this page's URL), or at /llms.txt for the index.
Open combobox.md