The Combobox component provides a searchable dropdown interface that allows users to filter and select from a list of options. It supports both single and multiple selection modes.
Basic usage
import { Combobox } from '@raystack/apsara';
function App() {
return (
<Combobox>
<Combobox.Input placeholder="Search..." />
<Combobox.Content>
<Combobox.Item value="react">React</Combobox.Item>
<Combobox.Item value="vue">Vue</Combobox.Item>
<Combobox.Item value="angular">Angular</Combobox.Item>
</Combobox.Content>
</Combobox>
);
}
Combobox (Root)
The controlled value of the combobox. Use single value for single selection, array for multiple.
The default value for uncontrolled usage.
onValueChange
(value: Value | Value[] | null) => void
Callback fired when the selected value changes.
onInputValueChange
(inputValue: string) => void
Callback fired when the input value changes.
Whether multiple selections are allowed.
Array of items for the combobox. Used for controlled item filtering.
Controls the open state of the combobox dropdown.
Callback fired when the open state changes.
Combobox.Input
Inherits all props from InputField component except trailingIcon, suffix, chips, and maxChipsVisible.
The label text displayed above the input.
Placeholder text for the input.
Whether the input is disabled.
Error message to display.
Helper text displayed below the input.
Combobox.Item
The value of the combobox item.
Whether the item is disabled.
Single selection
function SingleCombobox() {
const [value, setValue] = useState(null);
return (
<Combobox value={value} onValueChange={setValue}>
<Combobox.Input label="Framework" placeholder="Select a framework" />
<Combobox.Content>
<Combobox.Item value="react">React</Combobox.Item>
<Combobox.Item value="vue">Vue</Combobox.Item>
<Combobox.Item value="angular">Angular</Combobox.Item>
<Combobox.Item value="svelte">Svelte</Combobox.Item>
</Combobox.Content>
</Combobox>
);
}
Multiple selection
function MultiCombobox() {
const [values, setValues] = useState([]);
return (
<Combobox multiple value={values} onValueChange={setValues}>
<Combobox.Input label="Technologies" placeholder="Select technologies" />
<Combobox.Content>
<Combobox.Item value="react">React</Combobox.Item>
<Combobox.Item value="typescript">TypeScript</Combobox.Item>
<Combobox.Item value="nodejs">Node.js</Combobox.Item>
<Combobox.Item value="graphql">GraphQL</Combobox.Item>
</Combobox.Content>
</Combobox>
);
}
With groups and labels
<Combobox>
<Combobox.Input placeholder="Select a tool" />
<Combobox.Content>
<Combobox.Group>
<Combobox.Label>Frontend</Combobox.Label>
<Combobox.Item value="react">React</Combobox.Item>
<Combobox.Item value="vue">Vue</Combobox.Item>
</Combobox.Group>
<Combobox.Separator />
<Combobox.Group>
<Combobox.Label>Backend</Combobox.Label>
<Combobox.Item value="nodejs">Node.js</Combobox.Item>
<Combobox.Item value="python">Python</Combobox.Item>
</Combobox.Group>
</Combobox.Content>
</Combobox>
With input value tracking
function SearchableCombobox() {
const [value, setValue] = useState(null);
const [inputValue, setInputValue] = useState('');
return (
<div>
<Combobox
value={value}
onValueChange={setValue}
onInputValueChange={setInputValue}
>
<Combobox.Input placeholder="Search items" />
<Combobox.Content>
<Combobox.Item value="apple">Apple</Combobox.Item>
<Combobox.Item value="banana">Banana</Combobox.Item>
<Combobox.Item value="orange">Orange</Combobox.Item>
</Combobox.Content>
</Combobox>
<p>Searching for: {inputValue}</p>
</div>
);
}
With custom filtering
function FilteredCombobox() {
const [value, setValue] = useState(null);
const [inputValue, setInputValue] = useState('');
const allItems = [
{ value: 'apple', label: 'Apple' },
{ value: 'banana', label: 'Banana' },
{ value: 'orange', label: 'Orange' },
{ value: 'grape', label: 'Grape' }
];
const filteredItems = allItems.filter(item =>
item.label.toLowerCase().includes(inputValue.toLowerCase())
);
return (
<Combobox
value={value}
onValueChange={setValue}
onInputValueChange={setInputValue}
items={filteredItems.map(i => i.value)}
>
<Combobox.Input placeholder="Filter items" />
<Combobox.Content>
{filteredItems.map(item => (
<Combobox.Item key={item.value} value={item.value}>
{item.label}
</Combobox.Item>
))}
</Combobox.Content>
</Combobox>
);
}
With validation
<Combobox>
<Combobox.Input
label="Country"
error="Please select a country"
placeholder="Select your country"
/>
<Combobox.Content>
<Combobox.Item value="us">United States</Combobox.Item>
<Combobox.Item value="uk">United Kingdom</Combobox.Item>
<Combobox.Item value="ca">Canada</Combobox.Item>
</Combobox.Content>
</Combobox>
Multiple selection with chips
When using multiple selection mode, selected items are automatically displayed as removable chips in the input field.
<Combobox multiple value={['react', 'typescript']} onValueChange={setValues}>
<Combobox.Input label="Skills" placeholder="Add skills" />
<Combobox.Content>
<Combobox.Item value="react">React</Combobox.Item>
<Combobox.Item value="typescript">TypeScript</Combobox.Item>
<Combobox.Item value="nodejs">Node.js</Combobox.Item>
</Combobox.Content>
</Combobox>
Accessibility
- Built on Base UI Combobox primitive with full accessibility support.
- Supports keyboard navigation with arrow keys, Enter to select, and Escape to close.
- Proper ARIA attributes are automatically applied.
- Screen readers announce the number of available options and selected values.
- In multiple selection mode, chips can be removed using keyboard navigation.