Combo Box lets users choose one or more items from an options menu.
Use a Combo Box to offer a list of choices where multiple selection is possible.
Example
function Default() {
ComboBoxRenderer = () => {
const openSnackbar = useContext(SnackbarContext);
const options = [
{ value: 'melon', label: 'Melon' },
{ value: 'honeydew-melon', label: 'Honeydew melon' },
{ value: 'watermelon', label: 'Watermelon' },
{ value: 'apple', label: 'Apple' },
{ value: 'mango', label: 'Mango' }
];
const handleOnChange = (e) => {
openSnackbar({
title: `onChange happened. Value selected is ${e.value}`,
kind: 'positive'
});
};
return (
<ComboBox
options={options}
label="Fruits"
placeholder="Select some fruit"
onChange={handleOnChange}
/>
);
};
return (
<SnackbarProvider offsetTop={76}>
<ComboBoxRenderer />
</SnackbarProvider>
);
}
Usage Rules
When to use: Combo Box vs. List of Checkboxes vs. Select
The Combo Box component has feature parity with the Select component, and yet behaves slightly differently and offers many additional, but optional features. In general when both components could be used, make a decision based on the following criteria:
- If the options needs to be cleared after being selected, use the Combo Box component. Do not "hack" the Select component to clear the options by adding a "None Selected" option to the option list.
- Avoid mixing these two components in a composition (eg. filter toolbar), as that could lead to user confusion.
When it comes to the simple use case of selecting one or more options from a list (without additional features like creating option or searching within that list), follow this decision tree:
Select
- Users can select only one value from a list of mutually exclusive options.
- There are 5–15 options.
Checkbox List
- There’s a small number of options (5 or fewer) from which users can choose.
- There are 5–15 options.
- Seeing the selected item is more important than viewing the other available options.
- Space is not limited, or the list could be wrapped by a scroll container.
Radio Group
- Users can select only one value from a list of mutually exclusive options.
- There’s a small number of options (5 or fewer) from which users can choose.
Combo Box
- There are 5–15 options.
- There are more than 15 options.
- Users can select one or many from the list of options.
- Seeing an overview of all items, selected or not, aids in task completion.
- There’s not a clear or optimal default option.
- The list of options needs to be searchable.
- The user needs to be able to add new options.
Variants
Adaptive Width
If you would like to have the width of ComboBox adaptive to text, use the boolean prop adaptiveWidth
function adaptiveWidthBox() {
const options = [
{ value: 'melon', label: 'Melon' },
{ value: 'honeydew-melon', label: 'Honeydew melon' },
{ value: 'watermelon', label: 'Watermelon' },
{ value: 'fig', label: 'Fig' },
{ value: 'apple', label: 'Apple' },
{ value: 'mango', label: 'Mango' }
];
return (
<ComboBox
options={options}
label="Fruits"
placeholder="Select some fruit"
adaptiveWidth
/>
);
}
Adaptive Width with min-width for Options
If you are using adaptiveWidth
prop and would like to give the options a minimum width, you can use optionsMinWidth
function optionWithMinWidth() {
const options = [
{ value: 'melon', label: 'Melon' },
{ value: 'honeydew-melon', label: 'Honeydew melon' },
{ value: 'watermelon', label: 'Watermelon' },
{ value: 'fig', label: 'Fig' },
{ value: 'apple', label: 'Apple' },
{ value: 'mango', label: 'Mango' }
];
return (
<>
<p>Flexible width max-content</p>
<ComboBox
options={options}
label="Fruits"
placeholder="Select some fruit"
adaptiveWidth
optionsMinWidth={'max-content'}
/>
<p>Fixed min-width</p>
<ComboBox
options={options}
label="Fruits"
placeholder="Select some fruit"
adaptiveWidth
optionsMinWidth={300}
/>
</>
);
}
Async
Use the async
kind to load options from a remote data source as the user types.
function Async() {
const options = [
{ value: 'melon', label: 'Melon' },
{ value: 'apple', label: 'Apple' },
{ value: 'mango', label: 'Mango' }
];
const filterOptions = (inputValue) =>
options.filter((option) =>
option.label.toLowerCase().includes(inputValue.toLowerCase())
);
const promiseOptions = (inputValue) =>
new Promise((resolve) => {
setTimeout(() => {
resolve(filterOptions(inputValue));
}, 1000);
});
return (
<ComboBox
multiple
kind="async"
loadOptions={promiseOptions}
placeholder="Select some fruit"
aria-label="async-example"
/>
);
}
Async Creatable
The async-creatable
kind supports loading options from a remote data source and creating new options.
function AsyncCreatable() {
const options = [
{ value: 'melon', label: 'Melon' },
{ value: 'apple', label: 'Apple' },
{ value: 'mango', label: 'Mango' }
];
const filterOptions = (inputValue) =>
options.filter((option) =>
option.label.toLowerCase().includes(inputValue.toLowerCase())
);
const promiseOptions = (inputValue) =>
new Promise((resolve) => {
setTimeout(() => {
resolve(filterOptions(inputValue));
}, 1000);
});
const onChange = (newValue, actionMeta) => {
const { action, option } = actionMeta;
if (action === 'create-option') {
console.group('Option Created');
console.log(option);
console.groupEnd();
}
};
return (
<ComboBox
multiple
kind="async-creatable"
loadOptions={promiseOptions}
onChange={onChange}
placeholder="Select some fruit"
aria-label="async-creatable-example"
/>
);
}
You can block/unblock the scrolling when the ComboBox is open by using blockScroll
, default false
.
function customizeOptions() {
const options = [
{ value: 'melon', label: 'Melon' },
{ value: 'honeydew-melon', label: 'Honeydew melon' },
{ value: 'watermelon', label: 'Watermelon' },
{ value: 'fig', label: 'Fig' },
{ value: 'apple', label: 'Apple' },
{ value: 'mango', label: 'Mango' }
];
return (
<>
<ComboBox
options={options}
label="Fruits"
placeholder="Select some fruit"
blockScroll={true}
/>
</>
);
}
Clearable
Use isClearable
boolean prop to clear the selected option(s), you can also use onClear callback event when value is cleared.
function customizeOptions() {
const options = [
{ value: 'melon', label: 'Melon' },
{ value: 'honeydew-melon', label: 'Honeydew melon' },
{ value: 'watermelon', label: 'Watermelon' },
{ value: 'fig', label: 'Fig' },
{ value: 'apple', label: 'Apple' },
{ value: 'mango', label: 'Mango' }
];
return (
<ComboBox
options={options}
label="Fruits"
placeholder="Select some fruit"
isClearable={true}
onClear={() => console.log('onClear')}
/>
);
}
Creatable
The creatable
kind let users add new options along with the existing ones.
function Creatable() {
const defaultOptions = [
{ value: 'melon', label: 'Melon' },
{ value: 'apple', label: 'Apple' },
{ value: 'mango', label: 'Mango' }
];
const [value, setValue] = useState();
const [options, setOptions] = useState(defaultOptions);
const onCreateOption = (inputValue) => {
const newOption = {
value: `${inputValue}-${options.length}`,
label: inputValue
};
setOptions([...options, newOption]);
setValue(newOption);
console.group('Value Created');
console.log(newOption);
console.log('Running other code...');
console.groupEnd();
};
const onChange = (newValue) => {
setValue(newValue);
};
return (
<ComboBox
kind="creatable"
value={value}
options={options}
onChange={onChange}
onCreateOption={onCreateOption}
placeholder="Select some fruit"
aria-label="creatable-example"
/>
);
}
We can use indicatorCustomLabels
props which needs to match the StatusIndicatorLabelTypes
exported from StatusIndicator
component to show custom labels, when no custom labels are passed, default props are used.
function comboBoxWithCustomLabelledStatusIndicator() {
const options = [
{ value: 'melon', label: 'Melon' },
{ value: 'apple', label: 'Apple' },
{ value: 'mango', label: 'Mango' }
];
const customLabels = {
progress: 'Updating...',
positive: 'Updated!',
negative: 'Failed!'
};
return (
<ComboBox
options={options}
label="Fruits"
placeholder="Select some fruit"
indicatorType="positive"
indicatorWithLabel
indicatorCustomLabels={customLabels}
/>
);
}
Custom dataTestId
You can now test the component using custom testid using dataTestId
component.
function DataTestID() {
const options = [
{ value: 'melon', label: 'Melon', description: 'I am a sample text' },
{
value: 'apple',
label: 'Apple',
description: 'Apple a day keeps doctor away'
},
{ value: 'mango', label: 'Mango', description: 'The sweatest fruit' }
];
return (
<ComboBox
options={options}
label="Fruits"
placeholder="Select some fruit"
dataTestId="custom-test-id"
/>
);
}
Custom filter
You can customize the filter to search the options by adding the prop customFilterOption
to the component.
function LabelAndInfoText() {
const options = [
{ value: 'melon', label: 'Melon juice' },
{ value: 'apple', label: 'Apple cake' },
{ value: 'mango', label: 'Mango smoothie' }
];
const customFilterOption = (option, rawInput) => {
const words = rawInput.toLowerCase().split(' ');
return (
option.data.__isNew__ ||
words.reduce(
(acc, cur) => acc && option.label.toLowerCase().endsWith(cur),
true
)
);
};
return (
<ComboBox
options={options}
label="Fruits"
placeholder="Select some fruit"
customFilterOption={customFilterOption}
/>
);
}
Description
You can add description to the option using description
prop for items.
function LabelAndInfoText() {
const options = [
{ value: 'melon', label: 'Melon', description: 'I am a sample text' },
{
value: 'apple',
label: 'Apple',
description: 'Apple a day keeps doctor away'
},
{ value: 'mango', label: 'Mango', description: 'The sweatest fruit' }
];
return (
<ComboBox
options={options}
label="Fruits"
placeholder="Select some fruit"
/>
);
}
Disabled
function Disabled() {
const options = [
{ value: 'melon', label: 'Melon' },
{ value: 'apple', label: 'Apple' },
{ value: 'mango', label: 'Mango' }
];
const selectedOption = options[0];
return (
<ComboBox
disabled
value={selectedOption}
options={options}
placeholder="Select some fruit"
aria-label="disabled-example"
/>
);
}
Disabled Option
You can disable an option by setting the isDisabled
property to true.
function WithDisabledOption() {
const options = [
{ value: 'melon', label: 'Melon', isDisabled: true },
{ value: 'apple', label: 'Apple' },
{ value: 'mango', label: 'Mango' }
];
return (
<ComboBox
options={options}
placeholder="Select some fruit"
aria-label="disabled-option-example"
/>
);
}
You can customize the options by using formatOptionLabel and returning ReactNode
function customizeOptions() {
const options = [
{ value: 'melon', label: 'Melon', numberOfApps: 10 },
{ value: 'honeydew-melon', label: 'Honeydew melon', numberOfApps: 5 },
{ value: 'watermelon', label: 'Watermelon', numberOfApps: 8 },
{ value: 'fig', label: 'Fig', numberOfApps: 5 },
{ value: 'apple', label: 'Apple', numberOfApps: 3 },
{ value: 'mango', label: 'Mango', numberOfApps: 34 }
];
const formatOptionLabel = ({ value, label, numberOfApps }) => (
<div style={{ display: 'flex' }}>
<div>{label}</div>
<div
style={{ position: 'absolute', right: 30 }}
>{`${numberOfApps} Apps`}</div>
</div>
);
return (
<>
<ComboBox
options={options}
label="Fruits"
formatOptionLabel={formatOptionLabel}
placeholder="Select some fruit"
/>
</>
);
}
Free-text input with autocomplete
Use kind="input"
property to allow users type any kind of text even when it's not listed in the options. Such behavior is useful when you need a search-like input with autocomplete capabilities.
ComboBox component with kind="input"
does not support multiple
and noOptionsMessage
properties.
function customizeOptions() {
const options = [
{ value: 'melon', label: 'Melon' },
{ value: 'honeydew-melon', label: 'Honeydew melon' },
{ value: 'watermelon', label: 'Watermelon' },
{ value: 'fig', label: 'Fig' },
{ value: 'apple', label: 'Apple' },
{ value: 'mango', label: 'Mango' }
];
const [value, setValue] = useState();
const onChange = (newValue, actionMeta) => {
console.group('Value Changed');
console.log(newValue);
console.log(`action: ${actionMeta.action}`);
console.groupEnd();
setValue(newValue || undefined);
};
return (
<ComboBox
kind="input"
label="Fruits"
placeholder="Select some fruit"
options={options}
onChange={onChange}
value={value}
/>
);
}
Group Label Normalization
We have a boolean prop groupLabelNormalize
to Normalize the label casing of the group labels.
function Multi() {
const options = [
{
label: 'fruits',
options: [
{ value: 'melon', label: 'Melon' },
{ value: 'mango', label: 'Mango' }
]
},
{
label: 'other Fruits',
options: [
{ value: 'noneydew-melon', label: 'Honeydew melon' },
{ value: 'watermelon', label: 'Watermelon' },
{ value: 'apple', label: 'Apple' }
]
}
];
const onChange = (newValue, actionMeta) => {
console.group('Value Changed');
console.log(newValue);
console.log(`action: ${actionMeta.action}`);
console.groupEnd();
};
return (
<ComboBox
multiple
options={options}
onChange={onChange}
placeholder="Select some fruit"
aria-label="disabled-example"
label="Fruits"
optionalText="Optional"
groupLabelNormalize
/>
);
}
Icons
We support Icons with platform icons to the options menu
You can add the AppIcon by using the prop appInfo
which is an object that has two keys appId
and appName
.
We also support DuoTone icons as prop iconName
.
Finally the platform icons can be added by using the prop platform
that accepts a array of iconNames.
function Default() {
ComboBoxRenderer = () => {
const openSnackbar = useContext(SnackbarContext);
const options = [
{
value: 'leardroid',
label: 'Learn Droid',
appInfo: {
appId: "com.adjust-demo.leardroid",
appName: "Education - Learn Droid"
},
platform: [
'PlatformAndroidFilled',
'PlatformVizioTVFilled',
'PlatformIosFilled',
'PlatformWindowsFilled'
]
},
{
value: 'cryptoharvest',
label: 'Crypto Harvest',
appInfo: {
appId: "com.adjust-demo.cryptoharvest",
appName: "Finance - Crypto Harvest"
},
platform: [
'PlatformAndroidFilled',
'PlatformVizioTVFilled'
],
isDisabled: true
},
{
value: 'pocketbanking',
label: 'Pocket Banking',
appInfo: {
appId: "com.adjust-demo.pocketbanking",
appName: "Finance - Pocket Banking"
},
platform: [
'PlatformAndroidFilled',
]
},
{
value: 'piechart',
label: 'DuoTone PieChart',
iconName: 'DuoPieChart'
},
{
value: 'amazingmarket',
label: 'Amazing Market',
appInfo: {
appId: "com.adjust-demo.amazingmarket",
appName: "Shopping - Amazing Market"
},
platform: [
"PlatformWindowsFilled"
]
}
];
const handleOnChange = (e) => {
openSnackbar({
title: `onChange happened. Value selected is ${e.value}`,
kind: 'positive'
});
};
return (
<ComboBox
options={options}
label="Fruits"
placeholder="Select some fruit"
onChange={handleOnChange}
/>
);
};
return (
<SnackbarProvider offsetTop={76}>
<div style={{width: '250px'}}>
<ComboBoxRenderer />
</div>
</SnackbarProvider>
);
}
Loading State
You can add a spinner to ComboBox by adding isLoading
prop
function ComboBoxWithSpinner() {
const options = [
{ value: 'melon', label: 'Melon', numberOfApps: 10 },
{ value: 'honeydew-melon', label: 'Honeydew melon', numberOfApps: 5 },
{ value: 'watermelon', label: 'Watermelon', numberOfApps: 8 },
{ value: 'fig', label: 'Fig', numberOfApps: 5 },
{ value: 'apple', label: 'Apple', numberOfApps: 3 },
{ value: 'mango', label: 'Mango', numberOfApps: 34 }
];
return (
<>
<ComboBox
options={options}
label="Fruits"
isLoading
placeholder="Select some fruit"
/>
</>
);
}
Label and Info Text
Use an info text to give specific instructions to help users correctly fill in the data. You can also use it to clarify how the data will be used.
function LabelAndInfoText() {
const options = [
{ value: 'melon', label: 'Melon' },
{ value: 'apple', label: 'Apple' },
{ value: 'mango', label: 'Mango' }
];
return (
<ComboBox
multiple
options={options}
label="Fruits"
infoText="Want some melons?"
placeholder="Select some fruit"
/>
);
}
In cases when ComboBox is used in constrained spaces, it is possible to specify maxMenuHeight
for the options box.
function customizeOptions() {
const options = [
{ value: 'melon', label: 'Melon' },
{ value: 'honeydew-melon', label: 'Honeydew melon' },
{ value: 'watermelon', label: 'Watermelon' },
{ value: 'fig', label: 'Fig' },
{ value: 'apple', label: 'Apple' },
];
const maxMenuHeight = 30 * 3;
return (
<ComboBox
options={options}
label="Fruits"
placeholder="Select some fruit"
maxMenuHeight={maxMenuHeight}
/>
);
}
You can manage the placement of the dropdown by using menuPlacement
.
Has 3 variants: auto
, bottom
and top
and which is default to auto
.
function customizeOptions() {
const options = [
{ value: 'melon', label: 'Melon' },
{ value: 'honeydew-melon', label: 'Honeydew melon' },
{ value: 'watermelon', label: 'Watermelon' },
{ value: 'fig', label: 'Fig' },
{ value: 'apple', label: 'Apple' },
{ value: 'mango', label: 'Mango' }
];
return (
<>
<ComboBox
options={options}
label="Fruits"
placeholder="Select some fruit"
menuPlacement="top"
/>
</>
);
}
Multi Creatable
The multi-creatable
kind supports creating new multiple values.
function MultiCreatable() {
const [value, setValue] = useState([]);
const [inputValue, setInputValue] = useState('');
const handleChange = (value) => setValue(value);
const handleInput = (inputValue) => setInputValue(inputValue);
const handleKeyDown = (event) => {
if (!inputValue) return;
switch (event.key) {
case 'Enter':
case 'Tab':
setValue([
...value,
{
value: `${inputValue}-${value.length}`,
label: inputValue
}
]);
setInputValue('');
event.preventDefault();
}
};
return (
<ComboBox
kind="multi-creatable"
value={value}
inputValue={inputValue}
onChange={handleChange}
onInputChange={handleInput}
onKeyDown={handleKeyDown}
placeholder="Type anything and press `Enter`"
aria-label="creatable-example"
/>
);
}
Multi Selection
You can provide multiple
prop to enable multiple selection.
You also can do the range selection with Shift
key, by selecting an option first then press and hold the Shift
to select the range.
function Multi() {
const options = [
{ value: 'melon', label: 'Melon' },
{ value: 'noneydew-melon', label: 'Honeydew melon' },
{ value: 'watermelon', label: 'Watermelon' },
{ value: 'apple', label: 'Apple' },
{ value: 'mango', label: 'Mango' },
{ value: 'melon1', label: 'Melon1' },
{ value: 'noneydew-melon1', label: 'Honeydew melon1' },
{ value: 'watermelon1', label: 'Watermelon1' },
{ value: 'apple1', label: 'Apple1' },
{ value: 'mango1', label: 'Mango1' },
{ value: 'melon2', label: 'Melon2' },
{ value: 'noneydew-melon2', label: 'Honeydew melon2' },
{ value: 'watermelon2', label: 'Watermelon2' },
{ value: 'apple2', label: 'Apple2' },
{ value: 'mango2', label: 'Mango2' },
{ value: 'melon3', label: 'Melon3' },
{ value: 'noneydew-melon3', label: 'Honeydew melon3' },
{ value: 'watermelon3', label: 'Watermelon3' },
{ value: 'apple3', label: 'Apple3' },
{ value: 'mango3', label: 'Mango3' }
];
const onChange = (newValue, actionMeta) => {
console.group('Value Changed');
console.log(newValue);
console.log(`action: ${actionMeta.action}`);
console.groupEnd();
};
return (
<ComboBox
multiple
options={options}
onChange={onChange}
placeholder="Select some fruit"
aria-label="multi-example"
/>
);
}
Multi Selection with Select All Option
You can provide select all options by using selectAll
prop with custom label. It's an optional prop to allow display Select All
Option.
function Multi() {
const options = [
{ value: 'melon', label: 'Melon' },
{ value: 'noneydew-melon', label: 'Honeydew melon' },
{ value: 'watermelon', label: 'Watermelon' },
{ value: 'apple', label: 'Apple' },
{ value: 'mango', label: 'Mango' }
];
const selectAll = {
selectAllText: 'Select All',
unselectAllText: 'Unselect All'
};
return (
<ComboBox
multiple
options={options}
placeholder="Select some fruit"
aria-label="multi-example"
selectAll={selectAll}
/>
);
}
Multi Selection with limiter banner
You can provide multiLimit
prop to enable multiple selection with the limited selected options which can be displayed. The rest of selected options will be displayed as limiter banner.
function MultiLimit() {
const options = [
{ value: 'melon', label: 'Melon' },
{ value: 'noneydew-melon', label: 'Honeydew melon' },
{ value: 'watermelon', label: 'Watermelon' },
{ value: 'apple', label: 'Apple' },
{ value: 'mango', label: 'Mango' }
];
const onChange = (newValue, actionMeta) => {
console.group('Value Changed');
console.log(newValue);
console.log(`action: ${actionMeta.action}`);
console.groupEnd();
};
return (
<ComboBox
multiple
options={options}
onChange={onChange}
placeholder="Select some fruit"
aria-label="multi-example"
multiLimit={2}
/>
);
}
You can also provide multiLimit
prop as 'zero' to show only limiter banner.
Where first limiter banner will be displayed as value of selected.
function MultiLimit() {
const options = [
{ value: 'melon', label: 'Melon' },
{ value: 'noneydew-melon', label: 'Honeydew melon' },
{ value: 'watermelon', label: 'Watermelon' },
{ value: 'apple', label: 'Apple' },
{ value: 'mango', label: 'Mango' }
];
const onChange = (newValue, actionMeta) => {
console.group('Value Changed');
console.log(newValue);
console.log(`action: ${actionMeta.action}`);
console.groupEnd();
};
return (
<ComboBox
multiple
options={options}
onChange={onChange}
placeholder="Select some fruit"
aria-label="multi-example"
multiLimit="zero"
/>
);
}
Open Programmatically
You can use the isOpenDefault
prop to load the component open and the isOpen
prop to open the component programmatically. Please, remember in this case it will stay open until you change isOpen
to false.
Opening the ComboBox with either of these props set to true will not block scroll on the page.
function Open() {
const [isOpen, setIsOpen] = useState(undefined);
const [posts, setPosts] = useState([]);
const fetchPost = async () => {
const response = await fetch(
'https://hub.dummyapis.com/employee?noofRecords=100&idStarts=1001'
);
const data = await response.json();
setPosts(data);
};
useEffect(() => {
fetchPost();
}, []);
const options = posts.map(({ id, age }) => ({
value: age,
label: id
}));
const onInputChange = (newValue, actionMeta) => {
isOpen && setIsOpen(undefined);
console.group('Value Changed');
console.log(newValue);
console.log(`action: ${actionMeta.action}`);
console.groupEnd();
};
return (
<>
<Button
onClick={() => setIsOpen(isOpen ? undefined : true)}
label="toggle ComboBox"
/>
<ComboBox
options={options}
onInputChange={onInputChange}
placeholder="Select some fruit"
aria-label="open-example"
isOpen={isOpen}
/>
</>
);
}
Optional Text
You can show the field as optional or custom text by using the optionalText
prop as string.
function Multi() {
const options = [
{
label: 'fruits',
options: [
{ value: 'melon', label: 'Melon' },
{ value: 'mango', label: 'Mango' }
]
},
{
label: 'other Fruits',
options: [
{ value: 'noneydew-melon', label: 'Honeydew melon' },
{ value: 'watermelon', label: 'Watermelon' },
{ value: 'apple', label: 'Apple' }
]
}
];
const onChange = (newValue, actionMeta) => {
console.group('Value Changed');
console.log(newValue);
console.log(`action: ${actionMeta.action}`);
console.groupEnd();
};
return (
<ComboBox
multiple
options={options}
onChange={onChange}
placeholder="Select some fruit"
aria-label="disabled-example"
label="Fruits"
optionalText="Optional"
/>
);
}
You can add extra piece of information to the label by including a tooltip.
function LabelAndInfoText() {
const options = [
{ value: 'melon', label: 'Melon' },
{ value: 'apple', label: 'Apple' },
{ value: 'mango', label: 'Mango' }
];
return (
<ComboBox
multiple
options={options}
label="Fruits"
infoText="Want some melons?"
placeholder="Select some fruit"
labelIconTooltip={{
content: 'hello',
position: 'top'
}}
/>
);
}
You can change the position of the info text to be on top of the ComboBox.
Please avoid using the Info Text on top of the ComboBox when you also need the optional label.
function LabelAndInfoText() {
const options = [
{ value: 'melon', label: 'Melon' },
{ value: 'apple', label: 'Apple' },
{ value: 'mango', label: 'Mango' }
];
return (
<>
<ComboBox
multiple
options={options}
label="Fruits"
infoText="Info text really long that goes on to a second line"
infoTextTop
placeholder="Select some fruit"
/>
<ComboBox
multiple
options={options}
label="Fruits"
infoText="Info text"
infoTextTop
placeholder="Select some fruit"
/>
</>
);
}
We also support text tags inside the Info Text. Like strong
, b
, i
and a
.
function LabelAndInfoText() {
const options = [
{ value: 'melon', label: 'Melon' },
{ value: 'apple', label: 'Apple' },
{ value: 'mango', label: 'Mango' }
];
const infoText = `Info <b>text</b> really <i>long</i> that <a href='#'> goes </a> on to a <strong> second </strong>line`;
return (
<ComboBox
multiple
options={options}
label="Fruits"
infoText={infoText}
placeholder="Select some fruit"
/>
);
}
Readonly
ComboBox
component supports readOnly
property which can be used to prevent password managers from auto-filling internal's input element.
function Default() {
const options = [
{ value: 'melon', label: 'Melon' },
{ value: 'apple', label: 'Apple' },
{ value: 'mango', label: 'Mango' }
];
const [readOnly, setReadOnly] = useState(true);
const onFocus = useCallback(() => setReadOnly(false), []);
const onBlur = useCallback(() => setReadOnly(true), []);
return (
<ComboBox
options={options}
label="Fruits"
placeholder="Select some fruit"
readOnly={readOnly}
onFocus={onFocus}
onBlur={onBlur}
/>
);
}
Readonly options
You can use readonly
property in ComboBox
options to specify options that cannot be deleted or removed
function MultiWithReadonlyOptions() {
const options = [
{ value: 'melon', label: 'Melon', readonly: true },
{ value: 'mango', label: 'Mango', readonly: true },
{ value: 'noneydew-melon', label: 'Honeydew melon' },
{ value: 'watermelon', label: 'Watermelon' },
{ value: 'apple', label: 'Apple' }
];
const [value, setValue] = useState(options.slice(0, 2));
const handleChange = (value) => setValue(value);
return (
<ComboBox
multiple
value={value}
options={options}
onChange={handleChange}
placeholder="Select some fruit"
aria-label="multi-readonly-options-example"
/>
);
}
Status Indicating
function comboBoxStatusIndicator() {
const options = [
{ value: 'melon', label: 'Melon' },
{ value: 'apple', label: 'Apple' },
{ value: 'mango', label: 'Mango' }
];
return (
<ComboBox
options={options}
label="Fruits"
placeholder="Select some fruit"
indicatorType="positive"
/>
);
}
ComboBox component's Status Indicator withLabel
, here you can see defaultLabels
for indicatorType
.
defaultLabels: {
progress: 'Saving…',
positive: 'Saved!',
negative: 'Disconnected!'
}
function comboBoxStatusIndicatorWithLabel() {
const options = [
{ value: 'melon', label: 'Melon' },
{ value: 'apple', label: 'Apple' },
{ value: 'mango', label: 'Mango' }
];
return (
<ComboBox
options={options}
label="Fruits"
placeholder="Select some fruit"
indicatorType="positive"
indicatorWithLabel
/>
);
}
Searchable ComboBox
Set the prop isSearchable
as false
to disable this feature on the ComboBox, as this option is enabled by default.
function comboBoxStatusIndicator() {
const options = [
{ value: 'melon', label: 'Melon' },
{ value: 'apple', label: 'Apple' },
{ value: 'mango', label: 'Mango' }
];
return (
<ComboBox
options={options}
label="Fruits"
placeholder="Select some fruit"
isSearchable={false}
/>
);
}
Tabs Options
This allows to use Tabs inside ComboBox
function optionWithTabs() {
const tabItems = [
{ id: 'relevant', label: 'Relevant' },
{ id: 'all', label: 'All' }
];
const allOptions = {
all: [
{ value: 'melon', label: 'Melon' },
{ value: 'honeydew-melon', label: 'Honeydew melon' },
{ value: 'watermelon', label: 'Watermelon' }
],
relevant: [
{ value: 'fig', label: 'Fig' },
{ value: 'apple', label: 'Apple' },
{ value: 'mango', label: 'Mango' }
]
};
const selectAll = {
selectAllText: 'Select All',
unselectAllText: 'Unselect All'
};
return (
<ComboBox
options={allOptions}
label="Fruits"
placeholder="Select some fruit"
tabItems={tabItems}
initialSelectedTab="all"
multiple
selectAll={selectAll}
/>
);
}
To add Tooltip to an option you can add tooltipText
to your options
object. There are 3 props you can alter on the comboBox tooltip: delay: true
, position: 'right'
and offset: [0, 10]
. To alter these values you can add the prop optionsTooltipProps
which takes an object, ie: optionsTooltipProps={{position: 'left'}}
.
Use the formatSelectedOption
on the ComboBox to customize the content of the tooltip
shown when you hover on the selected option.
formatSelectedOption
is passed the selected option and should return a ReactNode
.
function optionTooltip() {
const options = [
{ value: 'melon', label: 'Melon', tooltipText: 'Melon is a fruit' },
{ value: 'apple', label: 'Apple' },
{ value: 'mango', label: 'Mango' }
];
const tooltipOverride = {
position: 'left',
delay: false,
offset: [0, 14]
};
const formatSelectedOption = ({ value, label, tooltipText }) => {
return (
<div>
<p>Selected option value is: {value}</p>
<p>Selected option label is: {label}</p>
{tooltipText ? <p>{tooltipText}</p> : null}
</div>
);
};
return (
<ComboBox
formatSelectedOption={formatSelectedOption}
options={options}
optionsTooltipProps={tooltipOverride}
label="Fruits"
placeholder="Select some fruit"
isSearchable={false}
/>
);
}
Truncate tokens
The prop truncateTokens
is a boolean to truncate the multi-value-chips in ComboBox multi-selection.
function Multi() {
const options = [
{ value: 'melon', label: 'Melon' },
{ value: 'noneydew-melon', label: 'Honeydew melon' },
{ value: 'watermelon', label: 'Watermelon' },
{ value: 'apple', label: 'Apple' },
{ value: 'mango', label: 'Mango' }
];
const onChange = (newValue, actionMeta) => {
console.group('Value Changed');
console.log(newValue);
console.log(`action: ${actionMeta.action}`);
console.groupEnd();
};
return (
<div style={{ width: 300 }}>
With truncate
<ComboBox
multiple
options={options}
onChange={onChange}
placeholder="Select some fruit"
aria-label="multi-example"
truncateTokens
multiLimit={2}
/>
Without truncate
<ComboBox
multiple
options={options}
onChange={onChange}
placeholder="Select some fruit"
aria-label="multi-example"
multiLimit={2}
/>
</div>
);
}
Virtualization
This component supports virtualization for the list of options. This improves rendering performance when handling large amounts of data.
We have tested that this component works with up to 100.000 items, with a minor delay when rendering the larger numbers. If you require a longer list of options, please consider using a different approach.
function Default() {
const options = new Array(1000).fill().map((_, index) => ({
value: `value_${index}`,
label: `Label ${index}`
}));
return (
<ComboBox
options={options}
label="Fruits"
placeholder="Select some fruit"
multiple
/>
);
}
Examples
Multi-creatable
Example for onBlur prop on multi-creatable
kind of ComboBox
function onBlurExample() {
const options = [
{ value: 'melon', label: 'Melon' },
{ value: 'apple', label: 'Apple' },
{ value: 'mango', label: 'Mango' }
];
const [value, setValue] = useState([]);
const [inputValue, setInputValue] = useState('');
const handleChange = (value) => setValue(value);
const handleInput = (inputValue) => setInputValue(inputValue);
const handleKeyDown = (event) => {
if (!inputValue) return;
switch (event.key) {
case 'Enter':
case 'Tab':
setValue([
...value,
{
value: `${inputValue}-${value.length}`,
label: inputValue
}
]);
setInputValue('');
event.preventDefault();
}
};
const handleBlur = () => {
if (inputValue.length) {
const newArray = [
...value,
{
value: inputValue,
label: inputValue
}
];
setValue(newArray);
setInputValue('');
event.preventDefault();
}
};
return (
<ComboBox
kind="multi-creatable"
value={value}
inputValue={inputValue}
onChange={handleChange}
onBlur={handleBlur}
onInputChange={setInputValue}
onKeyDown={handleKeyDown}
/>
);
}
Multi-creatable with Multi-selection
function MyTable() {
const columns = useMemo(
() => [
{
Header: 'First Name',
accessor: 'firstName',
Footer: () => 'Total'
},
{
Header: 'Last Name',
accessor: 'lastName',
width: 300,
Cell: () => {
const [value, setValue] = useState([]);
const [options, setOptions] = useState([]);
const [inputValue, setInputValue] = useState('');
const handleChange = (value) => setValue(value);
const handleInput = (inputValue) => setInputValue(inputValue);
const onCreateOption = (inputValue) => {
const newOption = {
value: `${inputValue}-${value.length}`,
label: inputValue
};
setValue([...value, newOption]);
setOptions([...options, newOption]);
setInputValue('');
event.preventDefault();
};
const handleKeyDown = (event) => {
if (!inputValue) return;
switch (event.key) {
case 'Enter':
case 'Tab':
onCreateOption(inputValue);
}
};
const selectAll = {
selectAllText: 'Select All',
unselectAllText: 'Unselect All'
};
return (
<ComboBox
kind="multi-creatable"
value={value}
inputValue={inputValue}
options={options}
onCreateOption={onCreateOption}
onChange={handleChange}
onInputChange={handleInput}
onKeyDown={handleKeyDown}
placeholder="Type anything and press `Enter`"
aria-label="creatable-example"
multiLimit={1}
selectAll={selectAll}
/>
);
}
}
],
[]
);
const data = [
{
firstName: 'Just a name',
lastName: 'Some lastName',
points: 10
},
{
firstName: 'Another name',
lastName: 'Another lastName',
points: 11
},
{
firstName: 'Random name',
lastName: 'Random lastName',
points: 700
}
];
return (
<div style={{ height: 300 }}>
<TableV2 data={data} columns={columns} />
</div>
);
}
Props
ComboBox
|
kind The kind of the combo box | "input" | "creatable" | "async" | "async-creatable" | "multi-creatable"
| — |
noOptionsMessage Function that returns text to display when there are no options | ((data: { inputValue: string; }) => string | null)
| — |
multiple Use this prop to allow multiple selection | | — |
label The label of the combo box | | — |
value The value of the select. Reflected by the selected option. | Option | OptionType[] | null
| — |
indicatorType The prop for indicatorType which is required to enable the indicator and shows the status based on its given type without label. | "negative" | "positive" | "warning" | "progress"
| — |
indicatorWithLabel This prop enables the display of a label next the status indicator. If no custom label is passed, it uses the default labels. | | — |
indicatorCustomLabels The prop for status indicator with custom labels with StatusIndicatorLabelTypes, if not passed uses default props that can be exported from StatusIndicator component. | StatusIndicatorLabelTypes
| — |
options Array of options, Array of group options, and Object of tab options, that populate the select menu.
The option has two required properties {label: string, value: string}.
It can be extended with any additional properties. | OptionType[] | GroupBase<OptionType>[] | Record<string, OptionType[] | GroupBase<OptionType>[]>
| — |
labelIconTooltip The labelIconTooltip content can either be pure text or any rich content wrapped in a React component
The labelIconTooltip content will be shown when hovering the label icon.
The labelIconTooltip position is used to define the positioning of the tooltip
When the labelIconTooltip content is not provided, the label icon will be not shown. | Omit<TooltipProps, "children">
| — |
loadOptions Function that returns a promise, which is the set of options to be used once the promise resolves | ((inputValue: string) => Promise<OptionType[]>)
| — |
onClear onClear callback event which triggers when combobox is cleared | | — |
onChange Handle change events when selecting an option.
The first argument is a new value.
The second argument is an object containing info about the change event.
For example, name of the action (select-option ) and the selected option. | ((value: OptionType | OnChangeValue<OptionType, MultiType>, action?: ActionMeta<OptionType>) => void) | ((value: OptionType, action?: ActionMeta<...>) => void) | undefined
| — |
inputValue Current input value | | — |
onInputChange Handle change events on the input.
The first argument is a new value.
The second argument is an object containing info about the change event.
For example, name of the action (select-option ) and the selected option. | ((newValue: string, actionMeta: InputActionMeta) => void)
| — |
onKeyDown Handle the keyboard enter event.
The argument is a keyboard event. | KeyboardEventHandler<HTMLDivElement>
| — |
onBlur Handle blur events on the control.
The argument is the event. | | — |
onFocus Handle focus events on the control.
The argument is the event. | | — |
onCreateOption If provided, this will be called with the input value
when a new option is created, and onChange will not be called.
Use this when you need more control over what happens when new options are created. | ((inputValue: string) => void)
| — |
loadingMessage Function that returns text to display when loading options | ((data: { inputValue: string; }) => string | null)
| — |
formatCreateOptionLabel Function that returns text to display in the create option | ((inputValue: string) => string)
| — |
isLoading Boolean prop to show spinner in ComboBox | | — |
infoText Instructions to help users correctly fill in the data | | — |
infoTextTop positions the infoText on top of the ComboBox | | — |
invalid Visually notify the user that the provided value is invalid | | — |
disabled Use this prop if the component should not be interactive | | — |
id Native HTML attribute: Link .
Internally, this component uses a random id. We use it to associate an input with a label element.
As the random id might break snapshot tests it can be overridden by setting an explicit id. | | — |
name Native HTML attribute: Link | | — |
placeholder This text is shown when no item is selected | | — |
aria-label Provide at least one label(visual or non-visual) property for better accessibility. Check Accessibility page for more info: Link | | — |
aria-labelledby Provide at least one label(visual or non-visual) property for better accessibility. Check Accessibility page for more info: Link | | — |
isOpen Controls if the comboBox is open or closed | | — |
isOpenDefault Loads the comboBox open | | — |
isSearchable enables search functionality | | — |
optionsTooltipProps enables tooltip message over options | | — |
multiLimit Limit for MultiSelect | | — |
adaptiveWidth Boolean value for ComboBox adaptive width | | — |
optionsMinWidth String or number value for ComboBox options min width | | — |
formatOptionLabel Formats option labels in the menu and control as React components | ((option: OptionType, labelMeta: FormatOptionLabelMeta<OptionType>) => ReactNode)
| — |
formatSelectedOption Customizes tooltip content shown when you hover on the selected option | ((option: OptionType) => ReactNode)
| — |
tabItems Array of tab items. See the structure of TabItemType | | — |
initialSelectedTab initial tabId of selected tab item | | — |
autoFocus Focus the control when it is mounted: Link | | — |
menuPlacement Placement prop for dropdown | "auto" | "top" | "bottom"
| — |
blockScroll The prop allows controlling the placement of the dropdown. | | — |
optionalText optionalText value to show the field is optional with custom text | | — |
readOnly Native HTML attribute: Link | | — |
isClearable Boolean prop to show the ClearIndicator | | — |
selectAll The prop allows to display Select All option with custom label. | | — |
dataTestId Data-testid for the component | | — |
truncateTokens Boolean to truncate the multi value chips | | — |
customFilterOption Function to customize search | ((option: FilterOptionOption<Option>, inputValue: string) => boolean) | null
| — |
groupLabelNormalize Boolean to set Grouplabel lowercase | | — |
maxMenuHeight Defines max-height for the menu list | | — |
css Add custom styles to this component. Use with caution. Learn more here: Link | | — |
inputFieldCss | | — |
data-{foo} Data attributes can be used by testing libraries to retrieve components or assert their existence | | — |
* - the prop is required. |