Skip to main content
Combobox combines a text input with a dropdown list, allowing users to filter options by typing or select from a list. It supports autocomplete, custom filtering, and virtualized rendering for large datasets.

Installation

yarn add @twilio-paste/combobox

Usage

import { Combobox } from '@twilio-paste/combobox';
import { Label } from '@twilio-paste/label';

const MyComponent = () => {
  const [value, setValue] = React.useState('');
  const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry'];
  
  return (
    <>
      <Label htmlFor="fruit">Choose a fruit</Label>
      <Combobox
        items={items}
        labelText="Fruit"
        selectedItem={value}
        onSelectedItemChange={({ selectedItem }) => setValue(selectedItem || '')}
      />
    </>
  );
};

Props

items
any[]
required
Array of items to display in the dropdown list.
labelText
string
required
Accessible label text for the combobox.
selectedItem
any
The currently selected item (controlled component).
onSelectedItemChange
(changes: { selectedItem: any }) => void
Callback fired when the selected item changes.
inputValue
string
The current input value (for controlled input).
onInputValueChange
(changes: { inputValue: string }) => void
Callback fired when the input value changes.
itemToString
(item: any) => string
Function to convert items to strings for display and filtering.
disabled
boolean
default:"false"
Disables the combobox.
hasError
boolean
default:"false"
Sets the combobox to an error state.
helpText
string | React.ReactNode
Help text displayed below the combobox.
optionTemplate
(item: any) => React.ReactNode
Custom template for rendering list items.
emptyState
React.ReactNode
Content to display when no items match the filter.
insertBefore
React.ReactNode
Element to insert before the input (like an icon).
insertAfter
React.ReactNode
Element to insert after the input.
autocomplete
boolean
default:"false"
Enables autocomplete behavior.
variant
'default' | 'inverse'
default:"'default'"
Visual style variant.
groupItemsBy
string
Property name to group items by.
groupLabelTemplate
(groupName: string) => React.ReactNode
Custom template for rendering group labels.
initialIsOpen
boolean
default:"false"
Whether the listbox is initially open.
element
string
default:"'COMBOBOX'"
Overrides the default element name for customization.

Examples

Basic Combobox

import { Combobox } from '@twilio-paste/combobox';
import { Label } from '@twilio-paste/label';

const countries = [
  'United States',
  'United Kingdom',
  'Canada',
  'Australia',
  'Germany',
  'France'
];

<>
  <Label htmlFor="country">Country</Label>
  <Combobox
    items={countries}
    labelText="Country"
    helpText="Select your country"
  />
</>

With Object Items

import { Combobox } from '@twilio-paste/combobox';

interface User {
  id: number;
  name: string;
  email: string;
}

const users: User[] = [
  { id: 1, name: 'John Doe', email: '[email protected]' },
  { id: 2, name: 'Jane Smith', email: '[email protected]' },
  { id: 3, name: 'Bob Johnson', email: '[email protected]' },
];

<Combobox
  items={users}
  labelText="Assign to"
  itemToString={(item) => (item ? item.name : '')}
  optionTemplate={(item) => (
    <div>
      <div>{item.name}</div>
      <div style={{ fontSize: '0.875rem', color: '#666' }}>
        {item.email}
      </div>
    </div>
  )}
/>

With Autocomplete

import { Combobox } from '@twilio-paste/combobox';

const [inputValue, setInputValue] = React.useState('');
const [filteredItems, setFilteredItems] = React.useState(items);

const handleInputChange = ({ inputValue }) => {
  setInputValue(inputValue);
  setFilteredItems(
    items.filter(item => 
      item.toLowerCase().includes(inputValue.toLowerCase())
    )
  );
};

<Combobox
  items={filteredItems}
  labelText="Search"
  inputValue={inputValue}
  onInputValueChange={handleInputChange}
  autocomplete
/>

With Error State

import { Combobox } from '@twilio-paste/combobox';
import { Label } from '@twilio-paste/label';

<>
  <Label htmlFor="category" required>
    Category
  </Label>
  <Combobox
    items={categories}
    labelText="Category"
    hasError
    helpText="Please select a category"
  />
</>

With Grouped Items

import { Combobox } from '@twilio-paste/combobox';

interface Product {
  name: string;
  category: string;
}

const products: Product[] = [
  { name: 'SMS', category: 'Messaging' },
  { name: 'WhatsApp', category: 'Messaging' },
  { name: 'Voice', category: 'Voice & Video' },
  { name: 'Video', category: 'Voice & Video' },
];

<Combobox
  items={products}
  labelText="Product"
  itemToString={(item) => (item ? item.name : '')}
  groupItemsBy="category"
/>

With Custom Empty State

import { Combobox } from '@twilio-paste/combobox';
import { Text } from '@twilio-paste/text';

<Combobox
  items={filteredItems}
  labelText="Search users"
  emptyState={
    <Text as="div" fontStyle="italic" color="colorTextWeak">
      No users found. Try a different search term.
    </Text>
  }
/>

Accessibility

  • Built on WAI-ARIA Combobox pattern
  • Supports full keyboard navigation (Arrow keys, Enter, Escape)
  • Announces number of available options to screen readers
  • Automatically manages focus between input and listbox
  • Supports aria-describedby for help text association
  • Implements proper ARIA roles and states
  • Provides clear visual focus indicators

Best Practices

  • Use Combobox when users need to search or filter a list of options
  • For simple selection without search, use Select instead
  • Provide clear, searchable labels in your items
  • Implement custom filtering for better performance with large datasets
  • Use itemToString to define how objects are displayed and filtered
  • Show helpful empty states when no results match
  • Consider grouping related items for better organization
  • Use autocomplete for predictable, finite lists
  • Validate selections server-side
  • Provide clear error messages when validation fails

Build docs developers (and LLMs) love