Overview
Combobox combines a text input with a dropdown menu, allowing users to search and filter options as they type. Selected items are displayed as dismissible tags, making it ideal for selecting multiple items with search functionality.
Basic Usage
import { Combobox } from 'grauity';
import { useState } from 'react';
function MyForm() {
const [value, setValue] = useState([]);
return (
<Combobox
name="tags"
label="Tags"
placeholder="Type to search..."
multiple
items={[
{ type: 'option', label: 'React', value: 'react' },
{ type: 'option', label: 'TypeScript', value: 'typescript' },
{ type: 'option', label: 'JavaScript', value: 'javascript' },
{ type: 'option', label: 'CSS', value: 'css' },
]}
value={value}
onChange={setValue}
/>
);
}
Single Selection
import { Combobox } from 'grauity';
import { useState } from 'react';
function SingleSelectCombobox() {
const [movie, setMovie] = useState(null);
const movies = [
{ type: 'option', label: 'Avatar', value: 'avatar' },
{ type: 'option', label: 'Avengers: Endgame', value: 'endgame' },
{ type: 'option', label: 'Avatar: The Way of Water', value: 'avatar2' },
{ type: 'option', label: 'Star Wars: The Force Awakens', value: 'starwars' },
];
return (
<Combobox
name="movie"
label="Favorite Movie"
placeholder="Search movies..."
multiple={false}
items={movies}
value={movie}
onChange={setMovie}
/>
);
}
Multiple Selection
import { Combobox } from 'grauity';
import { useState } from 'react';
function MultiSelectCombobox() {
const [skills, setSkills] = useState([]);
const skillOptions = [
{ type: 'option', label: 'React', value: 'react' },
{ type: 'option', label: 'Vue', value: 'vue' },
{ type: 'option', label: 'Angular', value: 'angular' },
{ type: 'option', label: 'Svelte', value: 'svelte' },
{ type: 'option', label: 'TypeScript', value: 'typescript' },
{ type: 'option', label: 'JavaScript', value: 'javascript' },
];
return (
<Combobox
name="skills"
label="Technical Skills"
placeholder="Type to search skills..."
multiple
items={skillOptions}
value={skills}
onChange={setSkills}
helpMessage="Select all that apply"
/>
);
}
By default, Combobox uses client-side filtering. The search matches against the label and description fields.
Custom Search (Server-side)
import { Combobox } from 'grauity';
import { useState, useEffect } from 'react';
function ServerSearchCombobox() {
const [value, setValue] = useState([]);
const [options, setOptions] = useState([]);
const [loading, setLoading] = useState(false);
const handleTextInputChange = async (searchText) => {
if (!searchText) {
setOptions([]);
return;
}
setLoading(true);
// Simulate API call
const results = await searchAPI(searchText);
setOptions(
results.map(item => ({
type: 'option',
label: item.name,
value: item.id,
}))
);
setLoading(false);
};
return (
<Combobox
name="search"
label="Search"
placeholder="Type to search..."
multiple
useDefaultSearchMethod={false}
items={options}
value={value}
onChange={setValue}
onTextInputChange={handleTextInputChange}
/>
);
}
Set useDefaultSearchMethod={false} to disable client-side filtering when implementing custom search logic.
Custom Tag Renderer
import { Combobox, Button } from 'grauity';
import { useState } from 'react';
function CustomTagCombobox() {
const [value, setValue] = useState([]);
return (
<Combobox
name="custom"
label="Custom Tags"
placeholder="Type to search..."
multiple
items={[
{ type: 'option', label: 'Option 1', value: '1' },
{ type: 'option', label: 'Option 2', value: '2' },
{ type: 'option', label: 'Option 3', value: '3' },
]}
value={value}
onChange={setValue}
renderValue={({ item, onDismiss }) => (
<Button
color="success"
size="small"
icon="close"
iconPosition="right"
onClick={onDismiss}
>
{item.label}
</Button>
)}
/>
);
}
Dismiss on Backspace
import { Combobox } from 'grauity';
import { useState } from 'react';
function BackspaceDismissCombobox() {
const [value, setValue] = useState([]);
return (
<Combobox
name="tags"
label="Tags"
placeholder="Type to add tags..."
multiple
shouldDismissOnBackspace
items={[
{ type: 'option', label: 'Tag 1', value: '1' },
{ type: 'option', label: 'Tag 2', value: '2' },
{ type: 'option', label: 'Tag 3', value: '3' },
]}
value={value}
onChange={setValue}
helpMessage="Press Backspace to remove the last tag"
/>
);
}
With Sections and Icons
import { Combobox } from 'grauity';
import { useState } from 'react';
function GroupedCombobox() {
const [value, setValue] = useState([]);
return (
<Combobox
name="movies"
label="Movies"
placeholder="Search movies..."
multiple
items={[
{ type: 'subheader', title: 'TOP RATED' },
{
type: 'option',
label: 'The Shawshank Redemption',
value: 'shawshank',
leftIcon: 'check-circle',
},
{
type: 'option',
label: 'The Godfather',
value: 'godfather',
leftIcon: 'check-circle',
},
{ type: 'divider' },
{ type: 'subheader', title: 'RECENT' },
{
type: 'option',
label: 'Oppenheimer',
value: 'oppenheimer',
leftIcon: 'check-circle',
},
]}
value={value}
onChange={setValue}
/>
);
}
Trigger Props
The name of the combobox field.
The label displayed above the combobox.
Placeholder text for the input field.
Whether the field is required.
Whether the combobox is disabled.
Helper text displayed below the combobox.
Error message displayed below the combobox.
Color theme. Options: 'brand' | 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info'
Callback fired when the input text changes.
Props passed to the Tag component for selected items.
Custom renderer for selected value tags.(props: {
index: number;
item: BaseItemOptionProps;
onDismiss: () => void;
}) => React.ReactNode
Whether pressing Backspace (when input is empty) should remove the last selected item.
Array of menu items. Same format as Dropdown items.
value
BaseItemOptionProps | BaseItemOptionProps[]
Currently selected value(s).
Whether multiple items can be selected.
Whether to use built-in client-side search. Set to false for custom search implementations.
Whether to show Apply and Clear All buttons.
applyOnOptionSelectInMultipleMode
Whether onChange is called immediately when selecting options in multiple mode.
Callback fired when selection changes.(value: BaseItemOptionProps | BaseItemOptionProps[]) => void
Callback fired when the dropdown closes.(value: BaseItemOptionProps | BaseItemOptionProps[]) => void
Menu positioning configuration:
width: Menu width (default: '300px')
fullWidth: Match trigger width (default: true)
TypeScript
import { ComboboxProps, BaseItemOptionProps } from 'grauity';
interface ComboboxProps {
name: string;
label?: string;
placeholder?: string;
isRequired?: boolean;
isDisabled?: boolean;
helpMessage?: string;
errorMessage?: string;
color?: string;
multiple?: boolean;
items: BaseItemProps[];
value?: BaseItemOptionProps | BaseItemOptionProps[];
onChange?: (value: any) => void;
onClose?: (value: any) => void;
onTextInputChange?: (text: string) => void;
useDefaultSearchMethod?: boolean;
renderValue?: (props: any) => React.ReactNode;
shouldDismissOnBackspace?: boolean;
tagProps?: Omit<TagProps, 'children'>;
menuProps?: {
width?: string;
fullWidth?: boolean;
};
}
Accessibility
- Keyboard navigation with arrow keys and Enter
- Screen reader support with proper ARIA attributes
- Focus management between input and menu
- Selected items are dismissible via keyboard
- Support for assistive technologies
When implementing custom search with useDefaultSearchMethod={false}, ensure your search function handles debouncing to avoid excessive API calls.
Best Practices
- Use for searchable selections: Ideal when users need to search through many options
- Debounce API calls: When using server-side search, debounce the search function
- Provide feedback: Show loading states during async operations
- Clear empty states: Display helpful messages when no results are found
- Tag management: Make it easy to review and remove selected items
Related Components
View the source code on GitHub: