DEVELOPMENT PREVIEWIn development

Combobox

A filterable single-select input (WAI-ARIA combobox + listbox pattern) built on the same overlay engine as Dropdown and Popover — portal rendering, collision-aware positioning, and the shared layer stack for Escape/outside-press dismissal.

Basic

Selected: react

Snippet
import { Combobox } from '@creo-team/buzz-ui/client'

const [framework, setFramework] = useState<string | null>('react')

<Combobox
	label="Framework"
	options={[
		{ value: 'react', label: 'React', description: 'A library for building user interfaces' },
		{ value: 'vue', label: 'Vue', description: 'The progressive JavaScript framework' },
		{ value: 'ember', label: 'Ember', disabled: true, description: 'Currently unavailable' },
	]}
	value={framework}
	onChange={setFramework}
/>

Uncontrolled, clearable, and required

Pick the country your team is based in.

onChange fired with: nothing yet — still "us" from defaultValue

Snippet
// Works uncontrolled too — just read the committed value via onChange
<Combobox
	label="Country"
	required
	options={countries}
	defaultValue="us"
	onChange={setCountry}
	helpText="Pick the country your team is based in."
/>

// Hide the (×) clear button
<Combobox options={countries} clearable={false} />

Async options and custom filtering

onInputChange fires on every keystroke — debounce it yourself and swap in fresh options for server-side search. Pass loading to swap the chevron for a spinner while a request is in flight. filter overrides the default case-insensitive substring match entirely (useful once you're filtering server-side and want the client to trust whatever `options` it's given).

Snippet
const [options, setOptions] = useState(initialOptions)
const [loading, setLoading] = useState(false)

<Combobox
	label="Search users"
	options={options}
	loading={loading}
	onInputChange={useDebouncedCallback(async query => {
		setLoading(true)
		setOptions(await searchUsers(query))
		setLoading(false)
	}, 300)}
	// Trust the server's ranking instead of re-filtering client-side
	filter={() => true}
/>

Forms

Pass name to render a hidden input carrying the committed value, so the selection posts like any other field — no client-side submit handler required.

Snippet
<form action={updateSettings}>
	<Combobox name="country" label="Country" options={countries} defaultValue="us" />
	<Button type="submit">Save</Button>
</form>

Keyboard

  • / move the highlight (skips disabled options)
  • Enter select the highlighted option
  • Escape close without selecting, reverting to the current value
  • Tab close and move on, same as Escape