Skip to content
Redpanda UI
Esc
↑↓navigate↵open⌘Jpreview
Made by eblairmckee
Edit on GitHub

Combobox

A searchable select input with autocomplete functionality.

Installation

bash
npx shadcn@latest add @redpanda/combobox
bash
pnpm dlx shadcn@latest add @redpanda/combobox
bash
yarn dlx shadcn@latest add @redpanda/combobox
bash
bunx shadcn@latest add @redpanda/combobox
bash
nubx shadcn@latest add @redpanda/combobox
bash
aube dlx shadcn@latest add @redpanda/combobox

When to use

Use this decision tree to determine when to use the Combobox component:

Usage

tsx
import { Combobox } from '@/components/ui/combobox';
tsx
'use client';
import React from 'react';import { Combobox } from '@/components/ui/combobox';
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() {  const [value, setValue] = React.useState('');
  return <Combobox options={frameworks} value={value} onChange={setValue} placeholder="Select framework..." />;}

Behavior

Combobox is built on Base UI Combobox. The input is an ARIA combobox: focus stays in the input while aria-activedescendant points at the highlighted option, so screen readers announce each option as you move through the list.

Key Action
ArrowDown / ArrowUp Open the list, then move the highlight. The highlight wraps around the list.
Enter Pick the highlighted option. With creatable, commit the typed text when nothing is highlighted, even with the list closed. Never submits a surrounding form while the list is open.
Escape Close the list. With the list closed, clear the value.
ArrowRight With the caret at the end of the text, pick the highlighted option.
Home / End Move the caret to the start or end of the text.
  • Typing filters options by label and value, and highlights the first match.
  • Opening the list highlights the selected option. Enter keeps it; clicking it again clears the value.
  • Deleting all text clears the value.
  • Closing the list without a pick restores the selected label. With creatable, text typed for a new option stays in the input while it has focus, so Enter can still commit it; leaving the field restores the selected label.
  • Disabled options can be highlighted and are announced as disabled, but can’t be picked.
  • While the list is open, the rest of the page is hidden from assistive technology.

Props

Combobox

PropType
optionsComboboxOption[]

Array of options to display in the dropdown.

TypeComboboxOption[]
value?string

The currently selected value.

Typestring
onChange(value: string) => void

Callback when the selected value changes.

Type(value: string) => void
placeholder?string

Placeholder text when no value is selected.

Typestring
disabled?boolean

Whether the combobox is disabled.

Typeboolean
clearable?boolean

Shows a clear (X) button while a value is selected.

Typeboolean
Defaulttrue
creatable?boolean

If true, offers a "Create" option at the end of the list when the typed text matches no existing option.

Typeboolean
onCreateOption?(value: string) => void

Callback when a new option is created via the create action. Receives the trimmed text, which is also passed to onChange.

Type(value: string) => void
onInputValueChange?(value: string) => void

Called when the user edits the input text. Drive async search from here; it does not fire when picking an option fills in its label.

Type(value: string) => void
isLoading?boolean

Shows an inline loading status and suppresses the empty state while async options load.

Typeboolean
emptyState?React.ReactNode

Replaces the "No options found." message. Ignored while isLoading.

TypeReact.ReactNode
renderOption?(option: ComboboxOption) => React.ReactNode

Renders the content of each option row. The selected check icon is still rendered by the component.

Type(option: ComboboxOption) => React.ReactNode
createLabel?string

Noun used in the create prompt (e.g. "tag", "context"). Displays as "Type to create a new {createLabel}..." when empty.

Typestring
Default"option"
start?React.ReactNode | null

Content for the start slot of the input. Defaults to a search icon. Pass null to hide.

TypeReact.ReactNode | null
Default<Search />
className?string

Additional CSS classes for the input container.

Typestring
onOpen?() => void

Callback when the dropdown opens.

Type() => void
onClose?() => void

Callback when the dropdown closes.

Type() => void
defaultOpen?boolean

Renders the list open on mount, focusing the input unless preventAutoFocusOnOpen is set.

Typeboolean
preventAutoFocusOnOpen?boolean

Keeps a defaultOpen combobox from focusing its input on mount.

Typeboolean
container?HTMLElement

Element to portal the list into. Defaults to the nearest portal container, then the document body.

TypeHTMLElement
testId?string

Set as data-testid on the dropdown popup.

Typestring
inputTestId?string

Set as data-testid on the input.

Typestring
id?string

Set on the inner input, so a sibling <label htmlFor> both associates with it and names it.

Typestring
aria-label?string

Names the control when no visible label points at it. Set on the inner input.

Typestring
aria-labelledby?string

Ids of the elements that name the control, for a label the component does not render.

Typestring

ComboboxOption

PropType
valuestring

Value passed to onChange when the option is picked.

Typestring
labelstring

Text shown in the list and in the input; used for filtering with value.

Typestring
group?string

Section heading. Options sharing a group are listed together, in first-seen order.

Typestring
disabled?boolean

Renders the option but blocks picking it.

Typeboolean
data?unknown

Arbitrary payload handed back to renderOption.

Typeunknown
testId?string

Set as data-testid on the option.

Typestring
groupTestId?string

Set as data-testid on the option group; the first option of a group sets it.

Typestring

Labeling

The trigger is an input with role="combobox", so it needs an accessible name of its own — a placeholder is not one. Give it an id and point a Label at it, or pass aria-label / aria-labelledby when the name lives in an element the component does not render.

tsx
<Label htmlFor="framework">Framework</Label><Combobox id="framework" onChange={setValue} options={frameworks} value={value} />

Examples

Basic Combobox

Form Integration

Creatable Options

Disabled State

Grouped Combobox

Timezone Picker

Pair onInputValueChange with a debounced fetch and feed the resulting options back in. Toggle isLoading while the request is in flight to show an inline spinner and suppress the default “No options found.” message. Results are still filtered by the typed text, the first match is highlighted as soon as results arrive, and the selected option keeps its label even when a later search no longer returns it.

tsx
<Combobox  isLoading={loading}  onChange={setValue}  onInputValueChange={setQuery}  options={options}  emptyState="No topics match that query."  value={value}/>

Rich Option Rows

Use renderOption to render each row as a two-line label + metadata cell. Attach arbitrary per-option data via the optional data field.

tsx
<Combobox  options={BROKERS}  renderOption={(option) => {    const meta = option.data as BrokerMeta | undefined;    return (      <div className="flex flex-col">        <span className="font-medium text-sm">{option.label}</span>        {meta ? (          <span className="text-subtle text-xs">            {meta.host} · {meta.dc}          </span>        ) : null}      </div>    );  }}  onChange={setValue}  value={value}/>

Disabled Items

Mark a ComboboxOption as disabled: true to render it as a non-selectable row. Compose with renderOption to surface the reason it’s unavailable.

tsx
const OPTIONS: ComboboxOption[] = [  { value: 'reader', label: 'Reader' },  { value: 'maintainer', label: 'Maintainer', disabled: true },  { value: 'admin', label: 'Admin', disabled: true },];

Credits

Recent changes

  • patchv1.2.0Pin shipped dependency floors to the version we develop against. Registry items now declare ranges like `^5.1.9` (the actual installed version) instead of collapsing to `^5.0.0`, so consumers start on the known-tested baseline while caret semantics still allow any compatible release within the same major.#133
  • minorv1.1.0Theme docs refresh, readability pass on semantic foregrounds, and consumer-facing Base UI regression fixes.#121
  • minorv1.0.0Post-Base-UI polish. Public API unchanged.#116
  • majorv1.0.0Migrate every Radix-based primitive to `@base-ui/react@^1.4.0` (Base UI).#114
  • minorv0.3.0Add theme-provider component to the registry with documentation and tests. Includes playground type improvements (export RegistryItem, remove as-const boilerplate) and docs site dark mode border color fix.#109
See full history →

Last updated on

Was this page helpful?