Combobox
Autocomplete input that filters a dropdown list as the user types. Built on React Aria ComboBox.
Uses React Aria ComboBox; role="combobox" on input with aria-expanded and aria-controls pointing to the listbox.
Preview
tsx
import { Combobox } from '@arshad-shah/cynosure-react';
import { useState } from 'react';
const languages = [
{ value: 'ts', label: 'TypeScript' },
{ value: 'js', label: 'JavaScript' },
{ value: 'py', label: 'Python' },
{ value: 'rs', label: 'Rust' },
{ value: 'go', label: 'Go' },
{ value: 'rb', label: 'Ruby' },
];
export default function Example() {
const [value, setValue] = useState<string | null>(null);
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem', width: '260px' }}>
<Combobox
aria-label="Programming language"
items={languages}
value={value}
onValueChange={setValue}
placeholder="Search languages…"
/>
{value && (
<p style={{ fontSize: '0.875rem', color: 'var(--color-fg-muted, #6b7280)', margin: 0 }}>
Selected: {value}
</p>
)}
</div>
);
}
The input and its open/close chevron sit as raised tiles inside a segmented track, matching the other inputs. variant tints the track.
Async filtering
Section titled “Async filtering”Control inputValue and items externally to implement server-side filtering.
Preview
tsx
import { Combobox, ComboboxEmpty } from '@arshad-shah/cynosure-react';
import { useState } from 'react';
const ALL_ITEMS = [
{ value: 'react', label: 'React' },
{ value: 'vue', label: 'Vue' },
{ value: 'angular', label: 'Angular' },
{ value: 'svelte', label: 'Svelte' },
{ value: 'solid', label: 'Solid' },
{ value: 'qwik', label: 'Qwik' },
];
export default function Example() {
const [inputValue, setInputValue] = useState('');
const filtered = ALL_ITEMS.filter((item) =>
item.label.toLowerCase().includes(inputValue.toLowerCase()),
);
return (
<div style={{ width: '260px' }}>
<Combobox
aria-label="Framework"
items={filtered}
inputValue={inputValue}
onInputChange={setInputValue}
placeholder="Type to filter…"
emptyState={<ComboboxEmpty>No frameworks match "{inputValue}"</ComboboxEmpty>}
/>
</div>
);
}
Multi-value (custom value)
Section titled “Multi-value (custom value)”Use allowsCustomValue to let users enter values not present in the list.
Preview
tsx
import { Combobox } from '@arshad-shah/cynosure-react';
const colors = [
{ value: 'red', label: 'Red' },
{ value: 'green', label: 'Green' },
{ value: 'blue', label: 'Blue' },
{ value: 'yellow', label: 'Yellow' },
];
export default function Example() {
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem', width: '260px' }}>
<Combobox
aria-label="Colour (or type your own)"
items={colors}
allowsCustomValue
placeholder="Pick or type a colour…"
/>
<p style={{ fontSize: '0.75rem', color: 'var(--color-fg-muted, #6b7280)', margin: 0 }}>
Custom values allowed — type anything and press Enter.
</p>
</div>
);
}
Disabled
Section titled “Disabled” Preview
tsx
import { Combobox } from '@arshad-shah/cynosure-react';
const options = [
{ value: 'a', label: 'Option A' },
{ value: 'b', label: 'Option B' },
];
export default function Example() {
return (
<div style={{ width: '260px' }}>
<Combobox
aria-label="Disabled combobox"
items={options}
defaultValue="a"
disabled
placeholder="Disabled…"
/>
</div>
);
}
PropTypeDefaultDescription
value
string|null
—
Controlled selected value.
The current value (controlled).
defaultValue
string|null
—
Uncontrolled initial value.
The default value (uncontrolled).
onValueChange
((value: T | null) => void)
—
Fires when the user selects an item (or clears the field, with `null`).
inputValue
string
—
Current text in the input — separate from the selected key.
defaultInputValue
string
—
Uncontrolled initial input text.
onInputChange
((value: string) => void)
—
Fires on every keystroke in the input.
placeholder
string
"Search…"
Placeholder rendered when the input is empty.
label
string
—
Visible label, also used as the trigger's accessible name when no aria-label is set.
aria-label
string
—
Aria label when no visual label is available.
Defines a string value that labels the current element.
size
"sm"|"md"|"lg"
"md"
Control size.
variant
"outline"|"filled"|"ghost"
"outline"
Visual treatment.
disabled
boolean
—
Disables interaction.
required
boolean
—
Marks the field as required for form submission.
invalid
boolean
—
Renders the invalid state and sets `aria-invalid`.
allowsCustomValue
boolean
—
Allow values the user types that aren't in the list.
items
(readonly ComboboxItemData<T>[] & Iterable<object>)
—
Data-driven items; overrides `children`.
The list of ComboBox items (controlled).
children
ReactNode
—
JSX children (`<ComboboxItem>`).
emptyState
ReactNode
—
Rendered when the filtered list is empty.
className
string
—
style
CSSProperties
—
name
string
—
The name of the input element, used when submitting an HTML form. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#htmlattrdefname).
id
string
—
The element's unique identifier. See [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/id).
ref
Ref<HTMLInputElement>
—
Accessibility
Section titled “Accessibility”- Built on React Aria ComboBox —
role="combobox"on the input witharia-expanded,aria-controls, andaria-activedescendantwired automatically. - Keyboard: Arrow keys open/navigate the list, Enter selects, Escape clears and closes.
- Empty state is announced to screen readers when no items match.
Recipes
Section titled “Recipes”- Use
onInputChange+itemsto implement server-side search with debouncing. - Use
allowsCustomValuewhen users may need to enter values not in the preset list (e.g. tag creation).