Combobox
An advanced autocomplete/combobox component built with Headless UI.
Import
import { Combobox, ComboboxOption, ComboboxLabel, ComboboxDescription } from '@/components/ui';
Basic Usage
'use client';
import { useState } from 'react';
import { Combobox, ComboboxOption, ComboboxLabel } from '@/components/ui';
const options = [
{ id: 1, name: 'Wade Cooper' },
{ id: 2, name: 'Arlene Mccoy' },
{ id: 3, name: 'Devon Webb' },
];
export function Example() {
const [selected, setSelected] = useState(null);
return (
<Combobox
value={selected}
onChange={setSelected}
options={options}
displayValue={(option) => option?.name}
placeholder="Search people..."
>
{(option) => (
<ComboboxOption value={option}>
<ComboboxLabel>{option.name}</ComboboxLabel>
</ComboboxOption>
)}
</Combobox>
);
}
Props
Combobox
| Prop |
Type |
Default |
Description |
value |
T |
- |
Selected value |
onChange |
(value: T) => void |
- |
Change handler |
options |
T[] |
required |
Available options |
displayValue |
(value: T) => string |
required |
Display value getter |
filter |
(value: T, query: string) => boolean |
- |
Custom filter function |
placeholder |
string |
- |
Placeholder text |
anchor |
'top' | 'bottom' |
'bottom' |
Dropdown position |
autoFocus |
boolean |
false |
Auto focus on mount |
disabled |
boolean |
false |
Disable combobox |
aria-label |
string |
- |
Accessibility label |
ComboboxOption
| Prop |
Type |
Description |
value |
T |
Option value |
disabled |
boolean |
Disable option |
With Description
const users = [
{ id: 1, name: 'Wade Cooper', email: 'wade@example.com' },
{ id: 2, name: 'Arlene Mccoy', email: 'arlene@example.com' },
];
<Combobox
value={selected}
onChange={setSelected}
options={users}
displayValue={(user) => user?.name}
placeholder="Search users..."
>
{(user) => (
<ComboboxOption value={user}>
<ComboboxLabel>{user.name}</ComboboxLabel>
<ComboboxDescription>{user.email}</ComboboxDescription>
</ComboboxOption>
)}
</Combobox>
Custom Filter
<Combobox
value={selected}
onChange={setSelected}
options={users}
displayValue={(user) => user?.name}
filter={(user, query) =>
user.name.toLowerCase().includes(query.toLowerCase()) ||
user.email.toLowerCase().includes(query.toLowerCase())
}
placeholder="Search by name or email..."
>
{(user) => (
<ComboboxOption value={user}>
<ComboboxLabel>{user.name}</ComboboxLabel>
</ComboboxOption>
)}
</Combobox>
With Icons
import { UserIcon } from '@/components/icons';
<Combobox
value={selected}
onChange={setSelected}
options={users}
displayValue={(user) => user?.name}
>
{(user) => (
<ComboboxOption value={user}>
<UserIcon data-slot="icon" />
<ComboboxLabel>{user.name}</ComboboxLabel>
</ComboboxOption>
)}
</Combobox>
With Avatar
import { Avatar } from '@/components/ui';
<Combobox
value={selected}
onChange={setSelected}
options={users}
displayValue={(user) => user?.name}
>
{(user) => (
<ComboboxOption value={user}>
<Avatar data-slot="avatar" src={user.avatar} name={user.name} size="sm" />
<ComboboxLabel>{user.name}</ComboboxLabel>
</ComboboxOption>
)}
</Combobox>
Async Loading
'use client';
import { useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import { Combobox, ComboboxOption, ComboboxLabel, Spinner } from '@/components/ui';
export function AsyncCombobox() {
const [query, setQuery] = useState('');
const [selected, setSelected] = useState(null);
const { data: options, isLoading } = useQuery({
queryKey: ['search', query],
queryFn: () => searchUsers(query),
enabled: query.length > 0,
});
return (
<Combobox
value={selected}
onChange={setSelected}
options={options ?? []}
displayValue={(user) => user?.name}
placeholder="Search users..."
>
{isLoading ? (
<div className="p-4 text-center">
<Spinner />
</div>
) : (
(user) => (
<ComboboxOption value={user}>
<ComboboxLabel>{user.name}</ComboboxLabel>
</ComboboxOption>
)
)}
</Combobox>
);
}
Dropdown Position
{/* Opens below (default) */}
<Combobox anchor="bottom" ... />
{/* Opens above */}
<Combobox anchor="top" ... />
Features
- Virtual scrolling - Efficiently handles large lists
- Keyboard navigation - Full keyboard support
- Filtering - Built-in or custom filtering
- Accessible - ARIA compliant
- Dark mode - Full dark mode support
- Animations - Smooth open/close transitions
When to Use
| Scenario |
Component |
| Simple ID/name mapping |
SearchableSelect |
| Custom option rendering |
Combobox |
| Few options |
Select |
| Async search |
Combobox |