Skip to Content

ComboBox

A dropdown list that allows users to type a value to filter the options.

import * as React from 'react';
import { ComboBox } from '@itwin/itwinui-react';

export default () => {
  const options = React.useMemo(
    () => [
      { label: 'Apple', value: 'apple' },
      { label: 'Banana', value: 'banana' },
      { label: 'Grapefruit', value: 'grapefruit' },
      { label: 'Lychee', value: 'lychee' },
      { label: 'Kiwi', value: 'kiwi' },
      { label: 'Orange', value: 'orange' },
    ],
    [],
  );

  return (
    <ComboBox
      options={options}
      inputProps={{ placeholder: 'Pick a fruit, any fruit' }}
    />
  );
};

The combobox component is a dropdown menu that allows filtering. Values can be selected either using mouse clicks or using the Enter key.

Variants

Controlled

The value property allows you to control of the selected value. If multiselect is enabled, value will be an array of values.

import * as React from 'react';
import { ComboBox } from '@itwin/itwinui-react';

export default () => {
  const [value, setValue] = React.useState('green');

  const options = React.useMemo(
    () => [
      { label: 'Red', value: 'red' },
      { label: 'Orange', value: 'orange' },
      { label: 'Yellow', value: 'yellow' },
      { label: 'Green', value: 'green' },
      { label: 'Blue', value: 'blue' },
      { label: 'Purple', value: 'purple' },
    ],
    [],
  );

  return (
    <ComboBox
      options={options}
      inputProps={{ placeholder: 'Select a color' }}
      value={value}
      onChange={setValue}
    />
  );
};

Messages

Add a message below the combobox with the message property.

import * as React from 'react';
import { ComboBox } from '@itwin/itwinui-react';

export default () => {
  const [value, setValue] = React.useState('');

  const options = React.useMemo(
    () => [
      { label: 'Helvetica', value: 'helvetica' },
      { label: 'Futura', value: 'futura' },
      { label: 'Verdana', value: 'verdana' },
      { label: 'Times New Roman', value: 'roman' },
      { label: 'Arial', value: 'arial' },
      { label: 'Rockwell', value: 'rockwell' },
      { label: 'Bodoni', value: 'bodoni' },
      { label: 'Garamond', value: 'garamond' },
    ],
    [],
  );

  return (
    <ComboBox
      options={options}
      message='This font will be used in your signature.'
      inputProps={{ placeholder: 'Choose a font' }}
      onChange={setValue}
      value={value}
    />
  );
};

Use the StatusMessage component if you want a more complex message. In the example below, StatusMessage is used to add an icon to the message.

import * as React from 'react';
import { ComboBox, StatusMessage } from '@itwin/itwinui-react';
import { SvgClock } from '@itwin/itwinui-icons-react';

export default () => {
  const [value, setValue] = React.useState('');

  const options = React.useMemo(
    () => [
      { label: '9:30 AM', value: '9:30' },
      { label: '10:00 AM', value: '10:00' },
      { label: '10:30 AM', value: '10:30' },
      { label: '1:00 PM', value: '1:00' },
      { label: '2:00 PM', value: '2:00' },
      { label: '2:30 PM', value: '2:30' },
      { label: '4:30 PM', value: '4:30' },
    ],
    [],
  );

  return (
    <ComboBox
      options={options}
      message={
        <StatusMessage startIcon={<SvgClock />}>
          Appointments run for 30 minutes.
        </StatusMessage>
      }
      inputProps={{ placeholder: 'Select appointment time' }}
      onChange={setValue}
      value={value}
    />
  );
};

Status

Comboboxes can have different statuses applied to them. A status will give the combobox a colored border and an associated icon below. The available statuses are:

  • positive
  • warning
  • negative
import * as React from 'react';
import { ComboBox } from '@itwin/itwinui-react';

export default () => {
  const [value, setValue] = React.useState('');

  const options = React.useMemo(
    () => [
      { label: 'Alabama', value: 'AL' },
      { label: 'Alaska', value: 'AK' },
      { label: 'Arizona', value: 'AZ' },
      { label: 'Arkansas', value: 'AR' },
      { label: 'California', value: 'CA' },
      { label: 'Colorado', value: 'CO' },
      { label: 'Connecticut', value: 'CT' },
      { label: 'Delaware', value: 'DE' },
      { label: 'Florida', value: 'FL' },
      { label: 'Georgia', value: 'GA' },
      { label: 'Hawaii', value: 'HI' },
      { label: 'Idaho', value: 'ID' },
      { label: 'Illinois', value: 'IL' },
      { label: 'Indiana', value: 'IN' },
      { label: 'Iowa', value: 'IA' },
      { label: 'Kansas', value: 'KS' },
      { label: 'Kentucky', value: 'KY' },
      { label: 'Louisiana', value: 'LA' },
      { label: 'Maine', value: 'ME' },
      { label: 'Maryland', value: 'MD' },
      { label: 'Massachusetts', value: 'MA' },
      { label: 'Michigan', value: 'MI' },
      { label: 'Minnesota', value: 'MN' },
      { label: 'Mississippi', value: 'MS' },
      { label: 'Missouri', value: 'MO' },
      { label: 'Montana', value: 'MT' },
      { label: 'Nebraska', value: 'NE' },
      { label: 'Nevada', value: 'NV' },
      { label: 'New Hampshire', value: 'NH' },
      { label: 'New Jersey', value: 'NJ' },
      { label: 'New Mexico', value: 'NM' },
      { label: 'New York', value: 'NY' },
      { label: 'North Carolina', value: 'NC' },
      { label: 'North Dakota', value: 'ND' },
      { label: 'Ohio', value: 'OH' },
      { label: 'Oklahoma', value: 'OK' },
      { label: 'Oregon', value: 'OR' },
      { label: 'Pennsylvania', value: 'PA' },
      { label: 'Rhode Island	', value: 'RI' },
      { label: 'South Carolina', value: 'SC' },
      { label: 'South Dakota', value: 'SD' },
      { label: 'Tennessee', value: 'TN' },
      { label: 'Texas', value: 'TX' },
      { label: 'Utah', value: 'UT' },
      { label: 'Vermont', value: 'VT' },
      { label: 'Virginia', value: 'VA' },
      { label: 'Washington', value: 'WA' },
      { label: 'West Virginia', value: 'WV' },
      { label: 'Wisconsin', value: 'WI' },
      { label: 'Wyoming', value: 'WY' },
    ],
    [],
  );

  return (
    <ComboBox
      options={options}
      inputProps={{ placeholder: 'Select your state' }}
      onChange={setValue}
      value={value}
      status={value === '' ? 'negative' : 'positive'}
      message={value === '' ? 'Required' : ''}
    />
  );
};

Multiselect

Set the property multiple to true to allow users to select more than one value.

import * as React from 'react';
import { ComboBox, Label, InputGrid } from '@itwin/itwinui-react';

export default () => {
  const options = React.useMemo(
    () => [
      { label: 'Apartments', value: 'apartments' },
      { label: 'Houses', value: 'houses' },
      { label: 'Lofts', value: 'lofts' },
      { label: 'Condos', value: 'condos' },
      { label: 'Townhomes', value: 'townhomes' },
    ],
    [],
  );

  const [selectedOptions, setSelectedOptions] = React.useState([
    'townhomes',
    'condos',
  ]);

  return (
    <InputGrid>
      <Label htmlFor='housing-input'> Select housing type </Label>
      <ComboBox
        options={options}
        inputProps={{ id: 'housing-input', placeholder: 'Housing type' }}
        multiple
        value={selectedOptions}
        onChange={(selected) => {
          setSelectedOptions(selected);
        }}
      />
    </InputGrid>
  );
};

When multiple is true, a clearFilterOnOptionToggle prop is available. Possible values:

  • true (default): Filter is cleared when an option is toggled. Useful when users would likely want to re-filter after toggling an option.
  • false: Filter is not cleared when an option is toggled. This is useful when users would likely want to toggle multiple options from the same filtered results.
import * as React from 'react';
import {
  ComboBox,
  Label,
  InputGrid,
  Flex,
  Divider,
  Checkbox,
} from '@itwin/itwinui-react';

export default () => {
  const options = React.useMemo(
    () => [
      { label: 'Apartments', value: 'apartments' },
      { label: 'Houses', value: 'houses' },
      { label: 'Lofts', value: 'lofts' },
      { label: 'Condos', value: 'condos' },
      { label: 'Townhomes', value: 'townhomes' },
    ],
    [],
  );

  const [selectedOptions, setSelectedOptions] = React.useState([
    'townhomes',
    'condos',
  ]);

  const [clearFilterOnOptionToggle, setClearFilterOnOptionToggle] =
    React.useState(true);

  return (
    <Flex id='main-story-container' flexDirection='column' alignItems='stretch'>
      <InputGrid>
        <Label htmlFor='housing-input'> Select housing type </Label>
        <ComboBox
          options={options}
          inputProps={{ id: 'housing-input', placeholder: 'Housing type' }}
          multiple
          value={selectedOptions}
          onChange={(selected) => {
            setSelectedOptions(selected);
          }}
          clearFilterOnOptionToggle={clearFilterOnOptionToggle}
        />
      </InputGrid>
      <Divider />
      <Checkbox
        checked={clearFilterOnOptionToggle}
        onChange={(e) => setClearFilterOnOptionToggle(e.target.checked)}
        label='clearFilterOnOptionToggle'
      />
    </Flex>
  );
};

Virtualized

If you expect the combobox to have a very long list of items, you can set enableVirtualization to true. This will use virtualization for the scrollable dropdown list.

import * as React from 'react';
import { ComboBox } from '@itwin/itwinui-react';

export default () => {
  const [value, setValue] = React.useState('');

  const options = React.useMemo(
    () => [
      { label: 'Afghanistan', value: 'AF' },
      { label: 'Ã…land Islands', value: 'AX' },
      { label: 'Albania', value: 'AL' },
      { label: 'Algeria', value: 'DZ' },
      { label: 'American Samoa', value: 'AS' },
      { label: 'Andorra', value: 'AD' },
      { label: 'Angola', value: 'AO' },
      { label: 'Anguilla', value: 'AI' },
      { label: 'Antarctica', value: 'AQ' },
      { label: 'Antigua and Barbuda', value: 'AG' },
      { label: 'Argentina', value: 'AR' },
      { label: 'Armenia', value: 'AM' },
      { label: 'Aruba', value: 'AW' },
      { label: 'Australia', value: 'AU' },
      { label: 'Austria', value: 'AT' },
      { label: 'Azerbaijan', value: 'AZ' },
      { label: 'Bahamas', value: 'BS' },
      { label: 'Bahrain', value: 'BH' },
      { label: 'Bangladesh', value: 'BD' },
      { label: 'Barbados', value: 'BB' },
      { label: 'Belarus', value: 'BY' },
      { label: 'Belgium', value: 'BE' },
      { label: 'Belize', value: 'BZ' },
      { label: 'Benin', value: 'BJ' },
      { label: 'Bermuda', value: 'BM' },
      { label: 'Bhutan', value: 'BT' },
      { label: 'Bolivia', value: 'BO' },
      { label: 'Bosnia and Herzegovina', value: 'BA' },
      { label: 'Botswana', value: 'BW' },
      { label: 'Bouvet Island', value: 'BV' },
      { label: 'Brazil', value: 'BR' },
      { label: 'British Indian Ocean Territory', value: 'IO' },
      { label: 'Brunei Darussalam', value: 'BN' },
      { label: 'Bulgaria', value: 'BG' },
      { label: 'Burkina Faso', value: 'BF' },
      { label: 'Burundi', value: 'BI' },
      { label: 'Cambodia', value: 'KH' },
      { label: 'Cameroon', value: 'CM' },
      { label: 'Canada', value: 'CA' },
      { label: 'Cape Verde', value: 'CV' },
      { label: 'Cayman Islands', value: 'KY' },
      { label: 'Central African Republic', value: 'CF' },
      { label: 'Chad', value: 'TD' },
      { label: 'Chile', value: 'CL' },
      { label: 'China', value: 'CN' },
      { label: 'Christmas Island', value: 'CX' },
      { label: 'Cocos (Keeling) Islands', value: 'CC' },
      { label: 'Colombia', value: 'CO' },
      { label: 'Comoros', value: 'KM' },
      { label: 'Congo', value: 'CG' },
      { label: 'Congo, The Democratic Republic of the', value: 'CD' },
      { label: 'Cook Islands', value: 'CK' },
      { label: 'Costa Rica', value: 'CR' },
      { label: "Cote D'Ivoire", value: 'CI' },
      { label: 'Croatia', value: 'HR' },
      { label: 'Cuba', value: 'CU' },
      { label: 'Cyprus', value: 'CY' },
      { label: 'Czech Republic', value: 'CZ' },
      { label: 'Denmark', value: 'DK' },
      { label: 'Djibouti', value: 'DJ' },
      { label: 'Dominica', value: 'DM' },
      { label: 'Dominican Republic', value: 'DO' },
      { label: 'Ecuador', value: 'EC' },
      { label: 'Egypt', value: 'EG' },
      { label: 'El Salvador', value: 'SV' },
      { label: 'Equatorial Guinea', value: 'GQ' },
      { label: 'Eritrea', value: 'ER' },
      { label: 'Estonia', value: 'EE' },
      { label: 'Ethiopia', value: 'ET' },
      { label: 'Falkland Islands (Malvinas)', value: 'FK' },
      { label: 'Faroe Islands', value: 'FO' },
      { label: 'Fiji', value: 'FJ' },
      { label: 'Finland', value: 'FI' },
      { label: 'France', value: 'FR' },
      { label: 'French Guiana', value: 'GF' },
      { label: 'French Polynesia', value: 'PF' },
      { label: 'French Southern Territories', value: 'TF' },
      { label: 'Gabon', value: 'GA' },
      { label: 'Gambia', value: 'GM' },
      { label: 'Georgia', value: 'GE' },
      { label: 'Germany', value: 'DE' },
      { label: 'Ghana', value: 'GH' },
      { label: 'Gibraltar', value: 'GI' },
      { label: 'Greece', value: 'GR' },
      { label: 'Greenland', value: 'GL' },
      { label: 'Grenada', value: 'GD' },
      { label: 'Guadeloupe', value: 'GP' },
      { label: 'Guam', value: 'GU' },
      { label: 'Guatemala', value: 'GT' },
      { label: 'Guernsey', value: 'GG' },
      { label: 'Guinea', value: 'GN' },
      { label: 'Guinea-Bissau', value: 'GW' },
      { label: 'Guyana', value: 'GY' },
      { label: 'Haiti', value: 'HT' },
      { label: 'Heard Island and Mcdonald Islands', value: 'HM' },
      { label: 'Holy See (Vatican City State)', value: 'VA' },
      { label: 'Honduras', value: 'HN' },
      { label: 'Hong Kong', value: 'HK' },
      { label: 'Hungary', value: 'HU' },
      { label: 'Iceland', value: 'IS' },
      { label: 'India', value: 'IN' },
      { label: 'Indonesia', value: 'ID' },
      { label: 'Iran, Islamic Republic Of', value: 'IR' },
      { label: 'Iraq', value: 'IQ' },
      { label: 'Ireland', value: 'IE' },
      { label: 'Isle of Man', value: 'IM' },
      { label: 'Israel', value: 'IL' },
      { label: 'Italy', value: 'IT' },
      { label: 'Jamaica', value: 'JM' },
      { label: 'Japan', value: 'JP' },
      { label: 'Jersey', value: 'JE' },
      { label: 'Jordan', value: 'JO' },
      { label: 'Kazakhstan', value: 'KZ' },
      { label: 'Kenya', value: 'KE' },
      { label: 'Kiribati', value: 'KI' },
      { label: "Korea, Democratic People'S Republic of", value: 'KP' },
      { label: 'Korea, Republic of', value: 'KR' },
      { label: 'Kuwait', value: 'KW' },
      { label: 'Kyrgyzstan', value: 'KG' },
      { label: "Lao People'S Democratic Republic", value: 'LA' },
      { label: 'Latvia', value: 'LV' },
      { label: 'Lebanon', value: 'LB' },
      { label: 'Lesotho', value: 'LS' },
      { label: 'Liberia', value: 'LR' },
      { label: 'Libyan Arab Jamahiriya', value: 'LY' },
      { label: 'Liechtenstein', value: 'LI' },
      { label: 'Lithuania', value: 'LT' },
      { label: 'Luxembourg', value: 'LU' },
      { label: 'Macao', value: 'MO' },
      { label: 'Macedonia, The Former Yugoslav Republic of', value: 'MK' },
      { label: 'Madagascar', value: 'MG' },
      { label: 'Malawi', value: 'MW' },
      { label: 'Malaysia', value: 'MY' },
      { label: 'Maldives', value: 'MV' },
      { label: 'Mali', value: 'ML' },
      { label: 'Malta', value: 'MT' },
      { label: 'Marshall Islands', value: 'MH' },
      { label: 'Martinique', value: 'MQ' },
      { label: 'Mauritania', value: 'MR' },
      { label: 'Mauritius', value: 'MU' },
      { label: 'Mayotte', value: 'YT' },
      { label: 'Mexico', value: 'MX' },
      { label: 'Micronesia, Federated States of', value: 'FM' },
      { label: 'Moldova, Republic of', value: 'MD' },
      { label: 'Monaco', value: 'MC' },
      { label: 'Mongolia', value: 'MN' },
      { label: 'Montserrat', value: 'MS' },
      { label: 'Morocco', value: 'MA' },
      { label: 'Mozambique', value: 'MZ' },
      { label: 'Myanmar', value: 'MM' },
      { label: 'Namibia', value: 'NA' },
      { label: 'Nauru', value: 'NR' },
      { label: 'Nepal', value: 'NP' },
      { label: 'Netherlands', value: 'NL' },
      { label: 'Netherlands Antilles', value: 'AN' },
      { label: 'New Caledonia', value: 'NC' },
      { label: 'New Zealand', value: 'NZ' },
      { label: 'Nicaragua', value: 'NI' },
      { label: 'Niger', value: 'NE' },
      { label: 'Nigeria', value: 'NG' },
      { label: 'Niue', value: 'NU' },
      { label: 'Norfolk Island', value: 'NF' },
      { label: 'Northern Mariana Islands', value: 'MP' },
      { label: 'Norway', value: 'NO' },
      { label: 'Oman', value: 'OM' },
      { label: 'Pakistan', value: 'PK' },
      { label: 'Palau', value: 'PW' },
      { label: 'Palestinian Territory, Occupied', value: 'PS' },
      { label: 'Panama', value: 'PA' },
      { label: 'Papua New Guinea', value: 'PG' },
      { label: 'Paraguay', value: 'PY' },
      { label: 'Peru', value: 'PE' },
      { label: 'Philippines', value: 'PH' },
      { label: 'Pitcairn', value: 'PN' },
      { label: 'Poland', value: 'PL' },
      { label: 'Portugal', value: 'PT' },
      { label: 'Puerto Rico', value: 'PR' },
      { label: 'Qatar', value: 'QA' },
      { label: 'Reunion', value: 'RE' },
      { label: 'Romania', value: 'RO' },
      { label: 'Russian Federation', value: 'RU' },
      { label: 'RWANDA', value: 'RW' },
      { label: 'Saint Helena', value: 'SH' },
      { label: 'Saint Kitts and Nevis', value: 'KN' },
      { label: 'Saint Lucia', value: 'LC' },
      { label: 'Saint Pierre and Miquelon', value: 'PM' },
      { label: 'Saint Vincent and the Grenadines', value: 'VC' },
      { label: 'Samoa', value: 'WS' },
      { label: 'San Marino', value: 'SM' },
      { label: 'Sao Tome and Principe', value: 'ST' },
      { label: 'Saudi Arabia', value: 'SA' },
      { label: 'Senegal', value: 'SN' },
      { label: 'Serbia and Montenegro', value: 'CS' },
      { label: 'Seychelles', value: 'SC' },
      { label: 'Sierra Leone', value: 'SL' },
      { label: 'Singapore', value: 'SG' },
      { label: 'Slovakia', value: 'SK' },
      { label: 'Slovenia', value: 'SI' },
      { label: 'Solomon Islands', value: 'SB' },
      { label: 'Somalia', value: 'SO' },
      { label: 'South Africa', value: 'ZA' },
      { label: 'South Georgia and the South Sandwich Islands', value: 'GS' },
      { label: 'Spain', value: 'ES' },
      { label: 'Sri Lanka', value: 'LK' },
      { label: 'Sudan', value: 'SD' },
      { label: 'Surilabel', value: 'SR' },
      { label: 'Svalbard and Jan Mayen', value: 'SJ' },
      { label: 'Swaziland', value: 'SZ' },
      { label: 'Sweden', value: 'SE' },
      { label: 'Switzerland', value: 'CH' },
      { label: 'Syrian Arab Republic', value: 'SY' },
      { label: 'Taiwan, Province of China', value: 'TW' },
      { label: 'Tajikistan', value: 'TJ' },
      { label: 'Tanzania, United Republic of', value: 'TZ' },
      { label: 'Thailand', value: 'TH' },
      { label: 'Timor-Leste', value: 'TL' },
      { label: 'Togo', value: 'TG' },
      { label: 'Tokelau', value: 'TK' },
      { label: 'Tonga', value: 'TO' },
      { label: 'Trinidad and Tobago', value: 'TT' },
      { label: 'Tunisia', value: 'TN' },
      { label: 'Turkey', value: 'TR' },
      { label: 'Turkmenistan', value: 'TM' },
      { label: 'Turks and Caicos Islands', value: 'TC' },
      { label: 'Tuvalu', value: 'TV' },
      { label: 'Uganda', value: 'UG' },
      { label: 'Ukraine', value: 'UA' },
      { label: 'United Arab Emirates', value: 'AE' },
      { label: 'United Kingdom', value: 'GB' },
      { label: 'United States', value: 'US' },
      { label: 'United States Minor Outlying Islands', value: 'UM' },
      { label: 'Uruguay', value: 'UY' },
      { label: 'Uzbekistan', value: 'UZ' },
      { label: 'Vanuatu', value: 'VU' },
      { label: 'Venezuela', value: 'VE' },
      { label: 'Viet Nam', value: 'VN' },
      { label: 'Virgin Islands, British', value: 'VG' },
      { label: 'Virgin Islands, U.S.', value: 'VI' },
      { label: 'Wallis and Futuna', value: 'WF' },
      { label: 'Western Sahara', value: 'EH' },
      { label: 'Yemen', value: 'YE' },
      { label: 'Zambia', value: 'ZM' },
      { label: 'Zimbabwe', value: 'ZW' },
    ],
    [],
  );

  return (
    <ComboBox
      options={options}
      inputProps={{ placeholder: 'Select a country' }}
      onChange={setValue}
      value={value}
      enableVirtualization
    />
  );
};

Usage

Labels

You can use a Label component alongside the combobox to add a label. Note that passing the same id to the label as htmlFor and to the combobox as id in inputProps will allow users to open the combobox by clicking on the label.

import * as React from 'react';
import { ComboBox, Label, InputGrid } from '@itwin/itwinui-react';

export default () => {
  const [breakfast, setBreakfast] = React.useState('');
  const [lunch, setLunch] = React.useState('');

  const breakfastOptions = React.useMemo(
    () => [
      { label: 'Oatmeal parfait', value: 'parfait' },
      { label: 'Waffle', value: 'waffle' },
      { label: 'Omelette', value: 'omelette' },
      { label: 'Breakfast sandwich', value: 'sandwich' },
    ],
    [],
  );

  const lunchOptions = React.useMemo(
    () => [
      { label: 'BLT', value: 'blt' },
      { label: 'Salad', value: 'salad' },
      { label: 'Chicken sandwich', value: 'chicken' },
      { label: 'Tortilla soup', value: 'soup' },
      { label: 'Fettucini alfredo', value: 'pasta' },
    ],
    [],
  );

  return (
    <>
      <InputGrid>
        <Label htmlFor='breakfast-input'>Breakfast</Label>
        <ComboBox
          options={breakfastOptions}
          value={breakfast}
          onChange={setBreakfast}
          inputProps={{
            id: 'breakfast-input', // passing id to inputProps so it can be used in Label htmlFor
            placeholder: 'Choose your meal',
          }}
        />
      </InputGrid>
      <InputGrid>
        <Label htmlFor='lunch-input'>Lunch</Label>
        <ComboBox
          options={lunchOptions}
          value={lunch}
          onChange={setLunch}
          inputProps={{
            id: 'lunch-input', // passing id to inputProps so it can be used in Label htmlFor
            placeholder: 'Choose your meal',
          }}
        />
      </InputGrid>
    </>
  );
};

Disabled items

To disable specific items in the list, add the disabled property to the options.

import * as React from 'react';
import { ComboBox } from '@itwin/itwinui-react';

export default () => {
  const [value, setValue] = React.useState('');

  const courses = React.useMemo(
    () => [
      { label: 'User Interface Design', value: '6620', hasPrereq: false },
      { label: 'Machine Learning', value: '6630', hasPrereq: false },
      { label: 'Quality Assurance', value: '5710', hasPrereq: true },
      { label: 'Data mining', value: '5130', hasPrereq: true },
      { label: 'Digital Forensics', value: '5350', hasPrereq: true },
      { label: 'Wireless and Mobile Networks', value: '3370', hasPrereq: true },
      {
        label: 'Software Reverse Engineering',
        value: '7720',
        hasPrereq: false,
      },
      { label: 'Web Application Development', value: '5000', hasPrereq: true },
      { label: 'Database Systems', value: '5120', hasPrereq: true },
    ],
    [],
  );

  const options = React.useMemo(
    () =>
      courses.map((course, index) => ({
        ...course,
        sublabel: `COMP ${course.value}${
          course.hasPrereq ? ' - needs pre-requisite' : ''
        }`,
        disabled: course.hasPrereq,
      })),
    [],
  );

  return (
    <ComboBox
      options={options}
      value={value}
      onChange={setValue}
      inputProps={{ placeholder: 'Select elective' }}
    />
  );
};

Loading

If you expect to take a while to retrieve the options, you can show skeletons in the options list while the items are loading. Use MenuItemSkeleton to achieve this as in the example below.

import * as React from 'react';
import { ComboBox, MenuItemSkeleton } from '@itwin/itwinui-react';

export default () => {
  const countriesList = React.useMemo(
    () => [
      { label: 'Afghanistan', value: 'AF' },
      { label: 'Ã…land Islands', value: 'AX' },
      { label: 'Albania', value: 'AL' },
      { label: 'Algeria', value: 'DZ' },
      { label: 'American Samoa', value: 'AS' },
      { label: 'Andorra', value: 'AD' },
      { label: 'Angola', value: 'AO' },
      { label: 'Anguilla', value: 'AI' },
      { label: 'Antarctica', value: 'AQ' },
      { label: 'Antigua and Barbuda', value: 'AG' },
      { label: 'Argentina', value: 'AR' },
      { label: 'Armenia', value: 'AM' },
      { label: 'Aruba', value: 'AW' },
      { label: 'Australia', value: 'AU' },
      { label: 'Austria', value: 'AT' },
      { label: 'Azerbaijan', value: 'AZ' },
      { label: 'Bahamas', value: 'BS' },
      { label: 'Bahrain', value: 'BH' },
      { label: 'Bangladesh', value: 'BD' },
      { label: 'Barbados', value: 'BB' },
      { label: 'Belarus', value: 'BY' },
      { label: 'Belgium', value: 'BE' },
      { label: 'Belize', value: 'BZ' },
      { label: 'Benin', value: 'BJ' },
      { label: 'Bermuda', value: 'BM' },
      { label: 'Bhutan', value: 'BT' },
      { label: 'Bolivia', value: 'BO' },
      { label: 'Bosnia and Herzegovina', value: 'BA' },
      { label: 'Botswana', value: 'BW' },
      { label: 'Bouvet Island', value: 'BV' },
      { label: 'Brazil', value: 'BR' },
      { label: 'British Indian Ocean Territory', value: 'IO' },
      { label: 'Brunei Darussalam', value: 'BN' },
      { label: 'Bulgaria', value: 'BG' },
      { label: 'Burkina Faso', value: 'BF' },
      { label: 'Burundi', value: 'BI' },
      { label: 'Cambodia', value: 'KH' },
      { label: 'Cameroon', value: 'CM' },
      { label: 'Canada', value: 'CA' },
      { label: 'Cape Verde', value: 'CV' },
      { label: 'Cayman Islands', value: 'KY' },
      { label: 'Central African Republic', value: 'CF' },
      { label: 'Chad', value: 'TD' },
      { label: 'Chile', value: 'CL' },
      { label: 'China', value: 'CN' },
      { label: 'Christmas Island', value: 'CX' },
      { label: 'Cocos (Keeling) Islands', value: 'CC' },
      { label: 'Colombia', value: 'CO' },
      { label: 'Comoros', value: 'KM' },
      { label: 'Congo', value: 'CG' },
      { label: 'Congo, The Democratic Republic of the', value: 'CD' },
      { label: 'Cook Islands', value: 'CK' },
      { label: 'Costa Rica', value: 'CR' },
      { label: "Cote D'Ivoire", value: 'CI' },
      { label: 'Croatia', value: 'HR' },
      { label: 'Cuba', value: 'CU' },
      { label: 'Cyprus', value: 'CY' },
      { label: 'Czech Republic', value: 'CZ' },
      { label: 'Denmark', value: 'DK' },
      { label: 'Djibouti', value: 'DJ' },
      { label: 'Dominica', value: 'DM' },
      { label: 'Dominican Republic', value: 'DO' },
      { label: 'Ecuador', value: 'EC' },
      { label: 'Egypt', value: 'EG' },
      { label: 'El Salvador', value: 'SV' },
      { label: 'Equatorial Guinea', value: 'GQ' },
      { label: 'Eritrea', value: 'ER' },
      { label: 'Estonia', value: 'EE' },
      { label: 'Ethiopia', value: 'ET' },
      { label: 'Falkland Islands (Malvinas)', value: 'FK' },
      { label: 'Faroe Islands', value: 'FO' },
      { label: 'Fiji', value: 'FJ' },
      { label: 'Finland', value: 'FI' },
      { label: 'France', value: 'FR' },
      { label: 'French Guiana', value: 'GF' },
      { label: 'French Polynesia', value: 'PF' },
      { label: 'French Southern Territories', value: 'TF' },
      { label: 'Gabon', value: 'GA' },
      { label: 'Gambia', value: 'GM' },
      { label: 'Georgia', value: 'GE' },
      { label: 'Germany', value: 'DE' },
      { label: 'Ghana', value: 'GH' },
      { label: 'Gibraltar', value: 'GI' },
      { label: 'Greece', value: 'GR' },
      { label: 'Greenland', value: 'GL' },
      { label: 'Grenada', value: 'GD' },
      { label: 'Guadeloupe', value: 'GP' },
      { label: 'Guam', value: 'GU' },
      { label: 'Guatemala', value: 'GT' },
      { label: 'Guernsey', value: 'GG' },
      { label: 'Guinea', value: 'GN' },
      { label: 'Guinea-Bissau', value: 'GW' },
      { label: 'Guyana', value: 'GY' },
      { label: 'Haiti', value: 'HT' },
      { label: 'Heard Island and Mcdonald Islands', value: 'HM' },
      { label: 'Holy See (Vatican City State)', value: 'VA' },
      { label: 'Honduras', value: 'HN' },
      { label: 'Hong Kong', value: 'HK' },
      { label: 'Hungary', value: 'HU' },
      { label: 'Iceland', value: 'IS' },
      { label: 'India', value: 'IN' },
      { label: 'Indonesia', value: 'ID' },
      { label: 'Iran, Islamic Republic Of', value: 'IR' },
      { label: 'Iraq', value: 'IQ' },
      { label: 'Ireland', value: 'IE' },
      { label: 'Isle of Man', value: 'IM' },
      { label: 'Israel', value: 'IL' },
      { label: 'Italy', value: 'IT' },
      { label: 'Jamaica', value: 'JM' },
      { label: 'Japan', value: 'JP' },
      { label: 'Jersey', value: 'JE' },
      { label: 'Jordan', value: 'JO' },
      { label: 'Kazakhstan', value: 'KZ' },
      { label: 'Kenya', value: 'KE' },
      { label: 'Kiribati', value: 'KI' },
      { label: "Korea, Democratic People'S Republic of", value: 'KP' },
      { label: 'Korea, Republic of', value: 'KR' },
      { label: 'Kuwait', value: 'KW' },
      { label: 'Kyrgyzstan', value: 'KG' },
      { label: "Lao People'S Democratic Republic", value: 'LA' },
      { label: 'Latvia', value: 'LV' },
      { label: 'Lebanon', value: 'LB' },
      { label: 'Lesotho', value: 'LS' },
      { label: 'Liberia', value: 'LR' },
      { label: 'Libyan Arab Jamahiriya', value: 'LY' },
      { label: 'Liechtenstein', value: 'LI' },
      { label: 'Lithuania', value: 'LT' },
      { label: 'Luxembourg', value: 'LU' },
      { label: 'Macao', value: 'MO' },
      { label: 'Macedonia, The Former Yugoslav Republic of', value: 'MK' },
      { label: 'Madagascar', value: 'MG' },
      { label: 'Malawi', value: 'MW' },
      { label: 'Malaysia', value: 'MY' },
      { label: 'Maldives', value: 'MV' },
      { label: 'Mali', value: 'ML' },
      { label: 'Malta', value: 'MT' },
      { label: 'Marshall Islands', value: 'MH' },
      { label: 'Martinique', value: 'MQ' },
      { label: 'Mauritania', value: 'MR' },
      { label: 'Mauritius', value: 'MU' },
      { label: 'Mayotte', value: 'YT' },
      { label: 'Mexico', value: 'MX' },
      { label: 'Micronesia, Federated States of', value: 'FM' },
      { label: 'Moldova, Republic of', value: 'MD' },
      { label: 'Monaco', value: 'MC' },
      { label: 'Mongolia', value: 'MN' },
      { label: 'Montserrat', value: 'MS' },
      { label: 'Morocco', value: 'MA' },
      { label: 'Mozambique', value: 'MZ' },
      { label: 'Myanmar', value: 'MM' },
      { label: 'Namibia', value: 'NA' },
      { label: 'Nauru', value: 'NR' },
      { label: 'Nepal', value: 'NP' },
      { label: 'Netherlands', value: 'NL' },
      { label: 'Netherlands Antilles', value: 'AN' },
      { label: 'New Caledonia', value: 'NC' },
      { label: 'New Zealand', value: 'NZ' },
      { label: 'Nicaragua', value: 'NI' },
      { label: 'Niger', value: 'NE' },
      { label: 'Nigeria', value: 'NG' },
      { label: 'Niue', value: 'NU' },
      { label: 'Norfolk Island', value: 'NF' },
      { label: 'Northern Mariana Islands', value: 'MP' },
      { label: 'Norway', value: 'NO' },
      { label: 'Oman', value: 'OM' },
      { label: 'Pakistan', value: 'PK' },
      { label: 'Palau', value: 'PW' },
      { label: 'Palestinian Territory, Occupied', value: 'PS' },
      { label: 'Panama', value: 'PA' },
      { label: 'Papua New Guinea', value: 'PG' },
      { label: 'Paraguay', value: 'PY' },
      { label: 'Peru', value: 'PE' },
      { label: 'Philippines', value: 'PH' },
      { label: 'Pitcairn', value: 'PN' },
      { label: 'Poland', value: 'PL' },
      { label: 'Portugal', value: 'PT' },
      { label: 'Puerto Rico', value: 'PR' },
      { label: 'Qatar', value: 'QA' },
      { label: 'Reunion', value: 'RE' },
      { label: 'Romania', value: 'RO' },
      { label: 'Russian Federation', value: 'RU' },
      { label: 'RWANDA', value: 'RW' },
      { label: 'Saint Helena', value: 'SH' },
      { label: 'Saint Kitts and Nevis', value: 'KN' },
      { label: 'Saint Lucia', value: 'LC' },
      { label: 'Saint Pierre and Miquelon', value: 'PM' },
      { label: 'Saint Vincent and the Grenadines', value: 'VC' },
      { label: 'Samoa', value: 'WS' },
      { label: 'San Marino', value: 'SM' },
      { label: 'Sao Tome and Principe', value: 'ST' },
      { label: 'Saudi Arabia', value: 'SA' },
      { label: 'Senegal', value: 'SN' },
      { label: 'Serbia and Montenegro', value: 'CS' },
      { label: 'Seychelles', value: 'SC' },
      { label: 'Sierra Leone', value: 'SL' },
      { label: 'Singapore', value: 'SG' },
      { label: 'Slovakia', value: 'SK' },
      { label: 'Slovenia', value: 'SI' },
      { label: 'Solomon Islands', value: 'SB' },
      { label: 'Somalia', value: 'SO' },
      { label: 'South Africa', value: 'ZA' },
      { label: 'South Georgia and the South Sandwich Islands', value: 'GS' },
      { label: 'Spain', value: 'ES' },
      { label: 'Sri Lanka', value: 'LK' },
      { label: 'Sudan', value: 'SD' },
      { label: 'Surilabel', value: 'SR' },
      { label: 'Svalbard and Jan Mayen', value: 'SJ' },
      { label: 'Swaziland', value: 'SZ' },
      { label: 'Sweden', value: 'SE' },
      { label: 'Switzerland', value: 'CH' },
      { label: 'Syrian Arab Republic', value: 'SY' },
      { label: 'Taiwan, Province of China', value: 'TW' },
      { label: 'Tajikistan', value: 'TJ' },
      { label: 'Tanzania, United Republic of', value: 'TZ' },
      { label: 'Thailand', value: 'TH' },
      { label: 'Timor-Leste', value: 'TL' },
      { label: 'Togo', value: 'TG' },
      { label: 'Tokelau', value: 'TK' },
      { label: 'Tonga', value: 'TO' },
      { label: 'Trinidad and Tobago', value: 'TT' },
      { label: 'Tunisia', value: 'TN' },
      { label: 'Turkey', value: 'TR' },
      { label: 'Turkmenistan', value: 'TM' },
      { label: 'Turks and Caicos Islands', value: 'TC' },
      { label: 'Tuvalu', value: 'TV' },
      { label: 'Uganda', value: 'UG' },
      { label: 'Ukraine', value: 'UA' },
      { label: 'United Arab Emirates', value: 'AE' },
      { label: 'United Kingdom', value: 'GB' },
      { label: 'United States', value: 'US' },
      { label: 'United States Minor Outlying Islands', value: 'UM' },
      { label: 'Uruguay', value: 'UY' },
      { label: 'Uzbekistan', value: 'UZ' },
      { label: 'Vanuatu', value: 'VU' },
      { label: 'Venezuela', value: 'VE' },
      { label: 'Viet Nam', value: 'VN' },
      { label: 'Virgin Islands, British', value: 'VG' },
      { label: 'Virgin Islands, U.S.', value: 'VI' },
      { label: 'Wallis and Futuna', value: 'WF' },
      { label: 'Western Sahara', value: 'EH' },
      { label: 'Yemen', value: 'YE' },
      { label: 'Zambia', value: 'ZM' },
      { label: 'Zimbabwe', value: 'ZW' },
    ],
    [],
  );

  const fetchOptions = async () => {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(
          countriesList.map((country) => ({
            ...country,
            sublabel: country.value,
            startIcon: (
              <img
                loading='lazy'
                style={{ width: 20, height: 15 }}
                src={`https://flagcdn.com/w20/${country.value.toLowerCase()}.png`}
                srcSet={`https://flagcdn.com/w40/${country.value.toLowerCase()}.png 2x`}
                alt=''
              />
            ),
          })),
        );
      }, 2000);
    });
  };

  const [options, setOptions] = React.useState([]);
  const [selectedValue, setSelectedValue] = React.useState();
  const [isLoading, setIsLoading] = React.useState(true);

  const onChange = React.useCallback((value) => {
    setSelectedValue(value);
  }, []);

  const emptyContent = React.useMemo(() => {
    return isLoading ? (
      <>
        {new Array(6).fill(null).map((_, index) => {
          return (
            <MenuItemSkeleton
              key={index}
              hasIcon
              hasSublabel
              contentWidth={`${Math.min(
                Math.max(Math.random() * 100, 25),
                60,
              )}%`}
            />
          );
        })}
      </>
    ) : (
      'No options found'
    );
  }, [isLoading]);

  return (
    <ComboBox
      inputProps={{ placeholder: 'Select a country' }}
      value={selectedValue}
      onChange={onChange}
      emptyStateMessage={emptyContent}
      options={options}
      onShow={React.useCallback(async () => {
        if (!isLoading) {
          return;
        }
        setOptions(await fetchOptions());
        setIsLoading(false);
      }, [isLoading])}
    />
  );
};

Custom renderer

The property itemRenderer allows you to customize the combobox more specifically. Note that for keyboard navigation to work, the returned element should use the id provided by this function. The isFocused state is calculated using this id and can be used for specifying the focus styling. If a MenuItem is returned, then focus styling is automatically handled.

import * as React from 'react';
import { ComboBox, MenuItem } from '@itwin/itwinui-react';

export default () => {
  const options = React.useMemo(
    () => [
      { label: 'Hour', value: 'hour' },
      { label: 'Day', value: 'day' },
      { label: 'Week', value: 'week' },
      { label: 'Month', value: 'month' },
      { label: 'Year', value: 'year' },
    ],
    [],
  );

  const [selectedValue, setSelectedValue] = React.useState('');

  const onChange = React.useCallback((value) => {
    setSelectedValue(value);
  }, []);

  const itemRenderer = React.useCallback(
    ({ value, label }, { isSelected, id }) => (
      <MenuItem key={id} id={id} isSelected={isSelected} value={value}>
        <em
          style={{
            fontWeight: isSelected ? 'bold' : undefined,
            textTransform: 'uppercase',
          }}
        >
          {label}
        </em>
      </MenuItem>
    ),
    [],
  );

  return (
    <ComboBox
      options={options}
      inputProps={{ placeholder: 'Select a country' }}
      value={selectedValue}
      onChange={onChange}
      itemRenderer={itemRenderer}
    />
  );
};

Props

Prop Description Default
options
Array of options that populate the dropdown list.
SelectOption<T>[]
message
Message shown below the combobox. Use StatusMessage component.
ReactNode
filterFunction
Function to customize the default filtering logic.
(options: SelectOption<T>[], inputValue: string) => SelectOption<T>[]
inputProps
Native input element props.
Omit<Omit<DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref"> & { ...; }, "as" | keyof InputProps> & InputProps & { ...; }
dropdownMenuProps
Props to customize dropdown menu behavior.
ClassAttributes<HTMLDivElement> & HTMLAttributes<HTMLDivElement> & Pick<PopoverOptions & { ...; } & Omit<...>, "middleware"> & Pick<...>
endIconProps
End icon props.
Omit<Omit<DetailedHTMLProps<HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & { ...; }, "as" | keyof ComboBoxEndIconProps> & ComboBoxEndIconProps & { ...; }
emptyStateMessage
Message shown when no options are available. If JSX.Element is provided, it will be rendered as is and won't be wrapped with MenuExtraContent.
ReactNode
'No options found'
itemRenderer
A custom item renderer can be specified to control the rendering.
For keyboard navigation to work, the returned element should use the id provided by this function. The isFocused state is calculated using this id and can be used for specifying the focus styling. If a MenuItem is returned, then focus styling is automatically handled.
(option: SelectOption<T>, states: { isSelected: boolean; isFocused: boolean; id: string; index: number; }) => Element
enableVirtualization
If enabled, virtualization is used for the scrollable dropdown list. Use it if you expect a very long list of items. @beta
boolean
false
onShow
Callback fired when dropdown menu is opened.
() => void
onHide
Callback fired when dropdown menu is closed.
() => void
multiple
Enable multiple selection.
boolean
false
value
Controlled value of ComboBox. If multiple is enabled, it is an array of values.
Pass null or undefined to reset the value. Apart from resetting the value:
  • value={null} will switch to/remain in the controlled state.
  • value={undefined} will switch to/remain in the uncontrolled state.
T | T[]
defaultValue
Default value of value that is set on initial render. This is useful when you don't want to maintain your own state but still want to control the initial value.
T | T[]
onChange
Callback fired when selected value changes.
((value: T) => void) | ((value: T[], event: MultipleOnChangeProps<T>) => void)
clearFilterOnOptionToggle
Only applicable when multiple is enabled.
If true, toggling an option will clear the filter. Useful when users would likely want to re-filter after toggling an option.
If false, the filter will remain as-is after toggling an option. Useful when users would likely want to toggle multiple options from the same filtered results.
boolean
true
status
"positive" | "warning" | "negative"
id
string
className
string
style
CSSProperties
ref
ForwardedRef<HTMLElement>