ComboBox
A ComboBox combines a text input with a listbox, allowing users to filter a list of options to items matching a query.
| Install | yarn add @diallink-corp/convergo-react-combobox |
|---|---|
| Version | 4.1.2 |
| Usage | import {ComboBox} from '@diallink-corp/convergo-react-combobox' |
Static Collection
The ComboBox component can either render static or dynamic collections of data. For simple combobox components that need to render a list of pre-defined data a static collection should be used. Static collections cannot be changed over time.
<ComboBox label='What is your favorite fruit?'>
<Item key='apples'>Apples</Item>
<Item key='bananas'>Bananas</Item>
<Item key='grapes'>Grapes</Item>
<Item key='oranges'>Oranges</Item>
</ComboBox>
<ComboBox label='What is your favorite fruit?'>
<Item key='apples'>Apples</Item>
<Item key='bananas'>Bananas</Item>
<Item key='grapes'>Grapes</Item>
<Item key='oranges'>Oranges</Item>
</ComboBox>
<ComboBox label="What is your favorite fruit?">
<Item key="apples">
Apples
</Item>
<Item key="bananas">
Bananas
</Item>
<Item key="grapes">
Grapes
</Item>
<Item key="oranges">
Oranges
</Item>
</ComboBox>
Dynamic Collection
For data that is non-static, such as data fetched from a backend, the ComboBox component supports dynamic collections. Dynamic collections support adding, updating and removing of the data that is being rendered.
function Example() {
const allFruits = [
{ id: 1, name: 'Apples' },
{ id: 2, name: 'Bananas' },
{ id: 3, name: 'Grapes' },
{ id: 4, name: 'Oranges' }
];
const [fruits, setFruits] = useState(allFruits);
return (
<ComboBox
label="What is your favorite fruit?"
items={fruits}
onInputChange={(input) =>
setFruits(allFruits.filter(({ name }) => name.includes(input)))}
>
{(item) => <Item key={item.id}>{item.name}</Item>}
</ComboBox>
);
}
function Example() {
const allFruits = [
{ id: 1, name: 'Apples' },
{ id: 2, name: 'Bananas' },
{ id: 3, name: 'Grapes' },
{ id: 4, name: 'Oranges' }
];
const [fruits, setFruits] = useState(allFruits);
return (
<ComboBox
label="What is your favorite fruit?"
items={fruits}
onInputChange={(input) =>
setFruits(
allFruits.filter(({ name }) =>
name.includes(input)
)
)}
>
{(item) => <Item key={item.id}>{item.name}</Item>}
</ComboBox>
);
}
function Example() {
const allFruits = [
{
id: 1,
name: 'Apples'
},
{
id: 2,
name: 'Bananas'
},
{
id: 3,
name: 'Grapes'
},
{
id: 4,
name: 'Oranges'
}
];
const [
fruits,
setFruits
] = useState(
allFruits
);
return (
<ComboBox
label="What is your favorite fruit?"
items={fruits}
onInputChange={(
input
) =>
setFruits(
allFruits
.filter((
{ name }
) =>
name
.includes(
input
)
)
)}
>
{(item) => (
<Item
key={item.id}
>
{item.name}
</Item>
)}
</ComboBox>
);
}
Asynchronous Fetching
The ComboBox component supports asynchronous fetching of data via the onLoadMoreItems prop. The combobox
component uses its built-in infinite scrolling to achieve this. This means that when the user reaches
the bottom of the list, the component will automatically load more data. If you provide an isLoading
prop, the ComboBox component will show a Spinner component, to indicate to the user, that more data is
being fetched.
function Example() {
const list = useAsyncListState({
async loadItems(state) {
let { cursor, signal, filterValue } = state;
if (cursor) {
cursor = cursor.replace(/^http:\/\//i, 'https://');
}
const response = await fetch(
cursor || `https://swapi.py4e.com/api/people/?search=`,
{ signal }
);
const { results, next } = await response.json();
return { ...state, items: results, cursor: next };
},
initialFilterValue: 'R5-D4'
});
return (
<ComboBox
label="Favorite Star Wars character?"
items={list.items}
onInputChange={list.setFilterValue}
loadingState={list.loadingState}
onLoadMoreItems={list.loadMoreItems}
defaultSelectedKey={'R5-D4'}
>
{(item) => <Item key={item.name}>{item.name}</Item>}
</ComboBox>
);
}
function Example() {
const list = useAsyncListState({
async loadItems(state) {
let { cursor, signal, filterValue } = state;
if (cursor) {
cursor = cursor.replace(/^http:\/\//i, 'https://');
}
const response = await fetch(
cursor ||
`https://swapi.py4e.com/api/people/?search=`,
{ signal }
);
const { results, next } = await response.json();
return { ...state, items: results, cursor: next };
},
initialFilterValue: 'R5-D4'
});
return (
<ComboBox
label="Favorite Star Wars character?"
items={list.items}
onInputChange={list.setFilterValue}
loadingState={list.loadingState}
onLoadMoreItems={list.loadMoreItems}
defaultSelectedKey={'R5-D4'}
>
{(item) => <Item key={item.name}>{item.name}</Item>}
</ComboBox>
);
}
function Example() {
const list =
useAsyncListState({
async loadItems(
state
) {
let {
cursor,
signal,
filterValue
} = state;
if (cursor) {
cursor = cursor
.replace(
/^http:\/\//i,
'https://'
);
}
const response =
await fetch(
cursor ||
`https://swapi.py4e.com/api/people/?search=`,
{ signal }
);
const {
results,
next
} =
await response
.json();
return {
...state,
items: results,
cursor: next
};
},
initialFilterValue:
'R5-D4'
});
return (
<ComboBox
label="Favorite Star Wars character?"
items={list.items}
onInputChange={list
.setFilterValue}
loadingState={list
.loadingState}
onLoadMoreItems={list
.loadMoreItems}
defaultSelectedKey={'R5-D4'}
>
{(item) => (
<Item
key={item.name}
>
{item.name}
</Item>
)}
</ComboBox>
);
}
Uncontrolled Value
By default, the ComboBox component handles its selection uncontrolled. In the uncontrolled variant you can
pass in a defaultSelectedKey to combobox a value by default. The key that is being referenced here is the
value that is passed to the Item component as a key prop, so in this case item.name.
function Example() {
const fruits = [
{ id: 1, name: 'Apples' },
{ id: 2, name: 'Bananas' },
{ id: 3, name: 'Grapes' },
{ id: 4, name: 'Oranges' }
];
return (
<ComboBox
label="What is your favorite fruit?"
items={fruits}
defaultSelectedKey="Grapes"
>
{(item) => <Item key={item.name}>{item.name}</Item>}
</ComboBox>
);
}
function Example() {
const fruits = [
{ id: 1, name: 'Apples' },
{ id: 2, name: 'Bananas' },
{ id: 3, name: 'Grapes' },
{ id: 4, name: 'Oranges' }
];
return (
<ComboBox
label="What is your favorite fruit?"
items={fruits}
defaultSelectedKey="Grapes"
>
{(item) => <Item key={item.name}>{item.name}</Item>}
</ComboBox>
);
}
function Example() {
const fruits = [
{
id: 1,
name: 'Apples'
},
{
id: 2,
name: 'Bananas'
},
{
id: 3,
name: 'Grapes'
},
{
id: 4,
name: 'Oranges'
}
];
return (
<ComboBox
label="What is your favorite fruit?"
items={fruits}
defaultSelectedKey="Grapes"
>
{(item) => (
<Item
key={item.name}
>
{item.name}
</Item>
)}
</ComboBox>
);
}
Controlled Value
The use the controlled selection variant, you can use the selectedKey and onSelectionChange props together.
function Example() {
const fruits = [
{id: 1, name: 'Apples'},
{id: 2, name: 'Bananas'},
{id: 3, name: 'Grapes'},
{id: 4, name: 'Oranges'}
];
const [{inputValue, selectedKey}, setFieldState] = useState({
inputValue: '',
selectedKey: null
});
const handleInputChange = (value) => {
setFieldState((prevState) => ({
inputValue: value,
selectedKey: value === '' ? null : prevState.selectedKey
}));
};
const handleSelectionChange = (key) => {
setFieldState({
inputValue: fruits.find((item) => item.id === key)?.name ?? '',
selectedKey: key
});
};
return (
<div>
<ComboBox
label='What is your favorite fruit?'
items={fruits.filter((fruit) => fruit.name.includes(inputValue))}
selectedKey={selectedKey}
inputValue={inputValue}
onInputChange={handleInputChange}
onSelectionChange={handleSelectionChange}
>
{(item) => <Item key={item.id}>{item.name}</Item>}
</ComboBox>
<div>Value: {selectedKey}</div>
</div>
);
}
function Example() {
const fruits = [
{ id: 1, name: 'Apples' },
{ id: 2, name: 'Bananas' },
{ id: 3, name: 'Grapes' },
{ id: 4, name: 'Oranges' }
];
const [{ inputValue, selectedKey }, setFieldState] =
useState({
inputValue: '',
selectedKey: null
});
const handleInputChange = (value) => {
setFieldState((prevState) => ({
inputValue: value,
selectedKey: value === ''
? null
: prevState.selectedKey
}));
};
const handleSelectionChange = (key) => {
setFieldState({
inputValue: fruits.find((item) =>
item.id === key
)?.name ?? '',
selectedKey: key
});
};
return (
<div>
<ComboBox
label="What is your favorite fruit?"
items={fruits.filter((fruit) =>
fruit.name.includes(inputValue)
)}
selectedKey={selectedKey}
inputValue={inputValue}
onInputChange={handleInputChange}
onSelectionChange={handleSelectionChange}
>
{(item) => <Item key={item.id}>{item.name}</Item>}
</ComboBox>
<div>Value: {selectedKey}</div>
</div>
);
}
function Example() {
const fruits = [
{
id: 1,
name: 'Apples'
},
{
id: 2,
name: 'Bananas'
},
{
id: 3,
name: 'Grapes'
},
{
id: 4,
name: 'Oranges'
}
];
const [
{
inputValue,
selectedKey
},
setFieldState
] = useState({
inputValue: '',
selectedKey: null
});
const handleInputChange =
(value) => {
setFieldState((
prevState
) => ({
inputValue:
value,
selectedKey:
value === ''
? null
: prevState
.selectedKey
}));
};
const handleSelectionChange =
(key) => {
setFieldState({
inputValue:
fruits.find((
item
) =>
item.id ===
key
)?.name ?? '',
selectedKey: key
});
};
return (
<div>
<ComboBox
label="What is your favorite fruit?"
items={fruits
.filter((
fruit
) =>
fruit.name
.includes(
inputValue
)
)}
selectedKey={selectedKey}
inputValue={inputValue}
onInputChange={handleInputChange}
onSelectionChange={handleSelectionChange}
>
{(item) => (
<Item
key={item.id}
>
{item.name}
</Item>
)}
</ComboBox>
<div>
Value:{' '}
{selectedKey}
</div>
</div>
);
}
Static Sections
To group items by sections you can wrap the items in a Section component.
<ComboBox label='What is your favorite fruit?'>
<Section title='Category A'>
<Item key='apples'>Apples</Item>
<Item key='bananas'>Bananas</Item>
</Section>
<Section title='Category B'>
<Item key='grapes'>Grapes</Item>
<Item key='oranges'>Oranges</Item>
</Section>
</ComboBox>
<ComboBox label='What is your favorite fruit?'>
<Section title='Category A'>
<Item key='apples'>Apples</Item>
<Item key='bananas'>Bananas</Item>
</Section>
<Section title='Category B'>
<Item key='grapes'>Grapes</Item>
<Item key='oranges'>Oranges</Item>
</Section>
</ComboBox>
<ComboBox label="What is your favorite fruit?">
<Section title="Category A">
<Item key="apples">
Apples
</Item>
<Item key="bananas">
Bananas
</Item>
</Section>
<Section title="Category B">
<Item key="grapes">
Grapes
</Item>
<Item key="oranges">
Oranges
</Item>
</Section>
</ComboBox>
Dynamic Sections
To render a group of sections dynamically you can create them from a hierarchical data structure.
function Example() {
const fruits = [
{
title: 'Category A',
items: [
{ id: 1, name: 'Apples' },
{ id: 2, name: 'Bananas' }
]
},
{
title: 'Category B',
items: [
{ id: 3, name: 'Grapes' },
{ id: 4, name: 'Oranges' }
]
}
];
return (
<ComboBox label="What is your favorite fruit?" items={fruits}>
{(section) => (
<Section
key={section.title}
title={section.title}
items={section.items}
>
{(item) => <Item key={item.name}>{item.name}</Item>}
</Section>
)}
</ComboBox>
);
}
function Example() {
const fruits = [
{
title: 'Category A',
items: [
{ id: 1, name: 'Apples' },
{ id: 2, name: 'Bananas' }
]
},
{
title: 'Category B',
items: [
{ id: 3, name: 'Grapes' },
{ id: 4, name: 'Oranges' }
]
}
];
return (
<ComboBox
label="What is your favorite fruit?"
items={fruits}
>
{(section) => (
<Section
key={section.title}
title={section.title}
items={section.items}
>
{(item) => (
<Item key={item.name}>{item.name}</Item>
)}
</Section>
)}
</ComboBox>
);
}
function Example() {
const fruits = [
{
title:
'Category A',
items: [
{
id: 1,
name: 'Apples'
},
{
id: 2,
name: 'Bananas'
}
]
},
{
title:
'Category B',
items: [
{
id: 3,
name: 'Grapes'
},
{
id: 4,
name: 'Oranges'
}
]
}
];
return (
<ComboBox
label="What is your favorite fruit?"
items={fruits}
>
{(section) => (
<Section
key={section
.title}
title={section
.title}
items={section
.items}
>
{(item) => (
<Item
key={item
.name}
>
{item.name}
</Item>
)}
</Section>
)}
</ComboBox>
);
}
Required
To indicate to the user that the ComboBox is required you can use the isRequired prop.
<ComboBox label='What is your favorite fruit?' isRequired>
<Item key='apples'>Apples</Item>
<Item key='bananas'>Bananas</Item>
<Item key='grapes'>Grapes</Item>
<Item key='oranges'>Oranges</Item>
</ComboBox>
<ComboBox label='What is your favorite fruit?' isRequired>
<Item key='apples'>Apples</Item>
<Item key='bananas'>Bananas</Item>
<Item key='grapes'>Grapes</Item>
<Item key='oranges'>Oranges</Item>
</ComboBox>
<ComboBox
label="What is your favorite fruit?"
isRequired
>
<Item key="apples">
Apples
</Item>
<Item key="bananas">
Bananas
</Item>
<Item key="grapes">
Grapes
</Item>
<Item key="oranges">
Oranges
</Item>
</ComboBox>
Validation
The ComboBox component has built-in accessible validation support. To indicate whether the field is valid or not
you can use the errorMessage prop.
function Example() {
const [value, setValue] = useState();
return (
<ComboBox
label='What is your favorite fruit?'
isRequired
errorMessage={!value && 'This field is required.'}
selectedKey={value}
onSelectionChange={setValue}
>
<Item key='apples'>Apples</Item>
<Item key='bananas'>Bananas</Item>
<Item key='grapes'>Grapes</Item>
<Item key='oranges'>Oranges</Item>
</ComboBox>
);
}
function Example() {
const [value, setValue] = useState();
return (
<ComboBox
label='What is your favorite fruit?'
isRequired
errorMessage={!value && 'This field is required.'}
selectedKey={value}
onSelectionChange={setValue}
>
<Item key='apples'>Apples</Item>
<Item key='bananas'>Bananas</Item>
<Item key='grapes'>Grapes</Item>
<Item key='oranges'>Oranges</Item>
</ComboBox>
);
}
function Example() {
const [
value,
setValue
] = useState();
return (
<ComboBox
label="What is your favorite fruit?"
isRequired
errorMessage={!value &&
'This field is required.'}
selectedKey={value}
onSelectionChange={setValue}
>
<Item key="apples">
Apples
</Item>
<Item key="bananas">
Bananas
</Item>
<Item key="grapes">
Grapes
</Item>
<Item key="oranges">
Oranges
</Item>
</ComboBox>
);
}
Description
To give further instructions or more verbose examples to the user about a ComboBox you can provide a description prop.
Avoid using error like tone of voice of the message. If an errorMessage prop is provided it will replace the description.
<ComboBox
label="What is your favorite fruit?"
description="An enumeration of the essential qualities of a thing or species"
>
<Item key="apples">Apples</Item>
<Item key="bananas">Bananas</Item>
<Item key="grapes">Grapes</Item>
<Item key="oranges">Oranges</Item>
</ComboBox>
<ComboBox
label="What is your favorite fruit?"
description="An enumeration of the essential qualities of a thing or species"
>
<Item key="apples">Apples</Item>
<Item key="bananas">Bananas</Item>
<Item key="grapes">Grapes</Item>
<Item key="oranges">Oranges</Item>
</ComboBox>
<ComboBox
label="What is your favorite fruit?"
description="An enumeration of the essential qualities of a thing or species"
>
<Item key="apples">
Apples
</Item>
<Item key="bananas">
Bananas
</Item>
<Item key="grapes">
Grapes
</Item>
<Item key="oranges">
Oranges
</Item>
</ComboBox>
Contextual Help
To offer the user contextual help, the ComboBox supports passing a contextualHelp prop, that accepts a ReactNode.
<ComboBox
label='What is your favorite fruit?'
contextualHelp={(
<ContextualHelp variant="info">
<Header>
<Heading>Lorem Ipsum</Heading>
</Header>
<Content>
<Text>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
</Text>
</Content>
</ContextualHelp>
)}
>
<Item key='apples'>Apples</Item>
<Item key='bananas'>Bananas</Item>
<Item key='grapes'>Grapes</Item>
<Item key='oranges'>Oranges</Item>
</ComboBox>
<ComboBox
label="What is your favorite fruit?"
contextualHelp={
<ContextualHelp variant="info">
<Header>
<Heading>Lorem Ipsum</Heading>
</Header>
<Content>
<Text>
Lorem ipsum dolor sit amet, consectetur
adipiscing elit.
</Text>
</Content>
</ContextualHelp>
}
>
<Item key="apples">Apples</Item>
<Item key="bananas">Bananas</Item>
<Item key="grapes">Grapes</Item>
<Item key="oranges">Oranges</Item>
</ComboBox>
<ComboBox
label="What is your favorite fruit?"
contextualHelp={
<ContextualHelp variant="info">
<Header>
<Heading>
Lorem Ipsum
</Heading>
</Header>
<Content>
<Text>
Lorem ipsum
dolor sit
amet,
consectetur
adipiscing
elit.
</Text>
</Content>
</ContextualHelp>
}
>
<Item key="apples">
Apples
</Item>
<Item key="bananas">
Bananas
</Item>
<Item key="grapes">
Grapes
</Item>
<Item key="oranges">
Oranges
</Item>
</ComboBox>
Disabled
The ComboBox component can be disabled via the isDisabled prop.
<ComboBox
label="What is your favorite fruit?"
isDisabled
defaultSelectedKey="grapes"
>
<Item key="apples">Apples</Item>
<Item key="bananas">Bananas</Item>
<Item key="grapes">Grapes</Item>
<Item key="oranges">Oranges</Item>
</ComboBox>
<ComboBox
label="What is your favorite fruit?"
isDisabled
defaultSelectedKey="grapes"
>
<Item key="apples">Apples</Item>
<Item key="bananas">Bananas</Item>
<Item key="grapes">Grapes</Item>
<Item key="oranges">Oranges</Item>
</ComboBox>
<ComboBox
label="What is your favorite fruit?"
isDisabled
defaultSelectedKey="grapes"
>
<Item key="apples">
Apples
</Item>
<Item key="bananas">
Bananas
</Item>
<Item key="grapes">
Grapes
</Item>
<Item key="oranges">
Oranges
</Item>
</ComboBox>
Read Only
The ComboBox component can be marked as read only via the isReadOnly prop.
<ComboBox
label="What is your favorite fruit?"
isReadOnly
defaultSelectedKey="bananas"
>
<Item key="apples">Apples</Item>
<Item key="bananas">Bananas</Item>
<Item key="grapes">Grapes</Item>
<Item key="oranges">Oranges</Item>
</ComboBox>
<ComboBox
label="What is your favorite fruit?"
isReadOnly
defaultSelectedKey="bananas"
>
<Item key="apples">Apples</Item>
<Item key="bananas">Bananas</Item>
<Item key="grapes">Grapes</Item>
<Item key="oranges">Oranges</Item>
</ComboBox>
<ComboBox
label="What is your favorite fruit?"
isReadOnly
defaultSelectedKey="bananas"
>
<Item key="apples">
Apples
</Item>
<Item key="bananas">
Bananas
</Item>
<Item key="grapes">
Grapes
</Item>
<Item key="oranges">
Oranges
</Item>
</ComboBox>
Menu Direction
By default, the menu of The ComboBox component opens to the bottom. If the window does not offer enough
space to fully open the menu, it automatically opens it to the top. However, you can control this behaviour
yourself via the placement prop.
<ComboBox label='What is your favorite fruit?' placement='top end'>
<Item key='apples'>Apples</Item>
<Item key='bananas'>Bananas</Item>
<Item key='grapes'>Grapes</Item>
<Item key='oranges'>Oranges</Item>
</ComboBox>
<ComboBox
label="What is your favorite fruit?"
placement="top end"
>
<Item key="apples">Apples</Item>
<Item key="bananas">Bananas</Item>
<Item key="grapes">Grapes</Item>
<Item key="oranges">Oranges</Item>
</ComboBox>
<ComboBox
label="What is your favorite fruit?"
placement="top end"
>
<Item key="apples">
Apples
</Item>
<Item key="bananas">
Bananas
</Item>
<Item key="grapes">
Grapes
</Item>
<Item key="oranges">
Oranges
</Item>
</ComboBox>
Menu Alignment
The menu is by default aligned to the start of the input component. If the content of the menu is longer
than the input that shows the value of The ComboBox component, the menu will outgrow the end of the input.
However, this behaviour can be reversed with the placement prop.
<ComboBox label='What is your favorite fruit?' placement='top end'>
<Item key='apples'>Apples</Item>
<Item key='bananas'>Bananas</Item>
<Item key='grapes'>Grapes</Item>
<Item key='oranges'>Oranges</Item>
</ComboBox>
<ComboBox
label="What is your favorite fruit?"
placement="top end"
>
<Item key="apples">Apples</Item>
<Item key="bananas">Bananas</Item>
<Item key="grapes">Grapes</Item>
<Item key="oranges">Oranges</Item>
</ComboBox>
<ComboBox
label="What is your favorite fruit?"
placement="top end"
>
<Item key="apples">
Apples
</Item>
<Item key="bananas">
Bananas
</Item>
<Item key="grapes">
Grapes
</Item>
<Item key="oranges">
Oranges
</Item>
</ComboBox>
Labeling
A ComboBox component should be labeled with a visual text through the label prop. If the ComboBox does not
include a textual label, an aria-label or aria-labelledby prop need to be provided to support assistive
technology such as screen readers.
<ComboBox label='What is your favorite fruit?'>
<Item key='apples'>Apples</Item>
<Item key='bananas'>Bananas</Item>
<Item key='grapes'>Grapes</Item>
<Item key='oranges'>Oranges</Item>
</ComboBox>
<ComboBox label='What is your favorite fruit?'>
<Item key='apples'>Apples</Item>
<Item key='bananas'>Bananas</Item>
<Item key='grapes'>Grapes</Item>
<Item key='oranges'>Oranges</Item>
</ComboBox>
<ComboBox label="What is your favorite fruit?">
<Item key="apples">
Apples
</Item>
<Item key="bananas">
Bananas
</Item>
<Item key="grapes">
Grapes
</Item>
<Item key="oranges">
Oranges
</Item>
</ComboBox>
Label Alignment
For languages that are read left-to-right (LTR), such as English, the label of The ComboBox component is displayed
on the left side of the input. For right-to-left (RTL) languages, such as Arabic, this is flipped. You can control
the position of the label through the labelPlacement prop.
<ComboBox label="What is your favorite fruit?" labelPlacement="top end">
<Item key="apples">Apples</Item>
<Item key="bananas">Bananas</Item>
<Item key="grapes">Grapes</Item>
<Item key="oranges">Oranges</Item>
</ComboBox>
<ComboBox
label="What is your favorite fruit?"
labelPlacement="top end"
>
<Item key="apples">Apples</Item>
<Item key="bananas">Bananas</Item>
<Item key="grapes">Grapes</Item>
<Item key="oranges">Oranges</Item>
</ComboBox>
<ComboBox
label="What is your favorite fruit?"
labelPlacement="top end"
>
<Item key="apples">
Apples
</Item>
<Item key="bananas">
Bananas
</Item>
<Item key="grapes">
Grapes
</Item>
<Item key="oranges">
Oranges
</Item>
</ComboBox>
Label Position
By default, the label of The ComboBox component is displayed above its input. With the labelPlacement prop
this placement can be adjusted to be on the side of the input.
<ComboBox label="What is your favorite fruit?" labelPlacement="side end">
<Item key="apples">Apples</Item>
<Item key="bananas">Bananas</Item>
<Item key="grapes">Grapes</Item>
<Item key="oranges">Oranges</Item>
</ComboBox>
<ComboBox
label="What is your favorite fruit?"
labelPlacement="side end"
>
<Item key="apples">Apples</Item>
<Item key="bananas">Bananas</Item>
<Item key="grapes">Grapes</Item>
<Item key="oranges">Oranges</Item>
</ComboBox>
<ComboBox
label="What is your favorite fruit?"
labelPlacement="side end"
>
<Item key="apples">
Apples
</Item>
<Item key="bananas">
Bananas
</Item>
<Item key="grapes">
Grapes
</Item>
<Item key="oranges">
Oranges
</Item>
</ComboBox>
Controlled Menu State
The open state of the menu can be controlled via the isOpen prop.
function Example() {
const [isOpen, setOpen] = useState(false);
const handleOpenChange = () => {
setOpen((prevOpen) => !prevOpen);
};
return (
<ComboBox
label="What is your favorite fruit?"
isOpen={isOpen}
onOpenChange={handleOpenChange}
>
<Item key="apples">Apples</Item>
<Item key="bananas">Bananas</Item>
<Item key="grapes">Grapes</Item>
<Item key="oranges">Oranges</Item>
</ComboBox>
);
}
function Example() {
const [isOpen, setOpen] = useState(false);
const handleOpenChange = () => {
setOpen((prevOpen) => !prevOpen);
};
return (
<ComboBox
label="What is your favorite fruit?"
isOpen={isOpen}
onOpenChange={handleOpenChange}
>
<Item key="apples">Apples</Item>
<Item key="bananas">Bananas</Item>
<Item key="grapes">Grapes</Item>
<Item key="oranges">Oranges</Item>
</ComboBox>
);
}
function Example() {
const [
isOpen,
setOpen
] = useState(false);
const handleOpenChange =
() => {
setOpen((
prevOpen
) => !prevOpen);
};
return (
<ComboBox
label="What is your favorite fruit?"
isOpen={isOpen}
onOpenChange={handleOpenChange}
>
<Item key="apples">
Apples
</Item>
<Item key="bananas">
Bananas
</Item>
<Item key="grapes">
Grapes
</Item>
<Item key="oranges">
Oranges
</Item>
</ComboBox>
);
}
Clear button
The
function Example() {
return (
<ComboBox label='What is your favorite fruit?' isClearable={false}>
<Item key='apples'>Apples</Item>
<Item key='bananas'>Bananas</Item>
<Item key='grapes'>Grapes</Item>
<Item key='oranges'>Oranges</Item>
</ComboBox>
);
}
function Example() {
return (
<ComboBox
label="What is your favorite fruit?"
isClearable={false}
>
<Item key="apples">Apples</Item>
<Item key="bananas">Bananas</Item>
<Item key="grapes">Grapes</Item>
<Item key="oranges">Oranges</Item>
</ComboBox>
);
}
function Example() {
return (
<ComboBox
label="What is your favorite fruit?"
isClearable={false}
>
<Item key="apples">
Apples
</Item>
<Item key="bananas">
Bananas
</Item>
<Item key="grapes">
Grapes
</Item>
<Item key="oranges">
Oranges
</Item>
</ComboBox>
);
}
Custom Value
You can support custom values in the ComboBox by passing the allowsCustomValue prop.
function Example() {
const [value, setValue] = useState();
return (
<ComboBox
label='What is your favorite fruit?'
allowsCustomValue
selectedKey={value}
onSelectionChange={setValue}
description={value}
>
<Item key='apples'>Apples</Item>
<Item key='bananas'>Bananas</Item>
<Item key='grapes'>Grapes</Item>
<Item key='oranges'>Oranges</Item>
</ComboBox>
);
}
function Example() {
const [value, setValue] = useState();
return (
<ComboBox
label='What is your favorite fruit?'
allowsCustomValue
selectedKey={value}
onSelectionChange={setValue}
description={value}
>
<Item key='apples'>Apples</Item>
<Item key='bananas'>Bananas</Item>
<Item key='grapes'>Grapes</Item>
<Item key='oranges'>Oranges</Item>
</ComboBox>
);
}
function Example() {
const [
value,
setValue
] = useState();
return (
<ComboBox
label="What is your favorite fruit?"
allowsCustomValue
selectedKey={value}
onSelectionChange={setValue}
description={value}
>
<Item key="apples">
Apples
</Item>
<Item key="bananas">
Bananas
</Item>
<Item key="grapes">
Grapes
</Item>
<Item key="oranges">
Oranges
</Item>
</ComboBox>
);
}
Slots
The ComboBox comes with pre-styled slots for components like avatars, texts, descriptions and keyboard shortcuts.
function Example() {
const list = useAsyncListState({
async loadItems(state) {
let { cursor, signal, filterValue } = state;
if (cursor) {
cursor = cursor.replace(/^http:\/\//i, 'https://');
}
const response = await fetch(
cursor || `https://swapi.py4e.com/api/people/?search=`,
{ signal }
);
const { results, next } = await response.json();
return { ...state, items: results, cursor: next };
}
});
return (
<ComboBox
label="Favorite Star Wars character?"
items={list.items}
onInputChange={list.setFilterValue}
loadingState={list.loadingState}
onLoadMoreItems={list.loadMoreItems}
>
{(item) => (
<Item key={item.name}>
<Avatar src="https://cdn-icons-png.flaticon.com/512/147/147144.png?w=360" />
<Text>{item.name}</Text>
<Description>Height: {item.height} Weight: {item.mass}</Description>
</Item>
)}
</ComboBox>
);
}
function Example() {
const list = useAsyncListState({
async loadItems(state) {
let { cursor, signal, filterValue } = state;
if (cursor) {
cursor = cursor.replace(/^http:\/\//i, 'https://');
}
const response = await fetch(
cursor ||
`https://swapi.py4e.com/api/people/?search=`,
{ signal }
);
const { results, next } = await response.json();
return { ...state, items: results, cursor: next };
}
});
return (
<ComboBox
label="Favorite Star Wars character?"
items={list.items}
onInputChange={list.setFilterValue}
loadingState={list.loadingState}
onLoadMoreItems={list.loadMoreItems}
>
{(item) => (
<Item key={item.name}>
<Avatar src="https://cdn-icons-png.flaticon.com/512/147/147144.png?w=360" />
<Text>{item.name}</Text>
<Description>
Height: {item.height} Weight: {item.mass}
</Description>
</Item>
)}
</ComboBox>
);
}
function Example() {
const list =
useAsyncListState({
async loadItems(
state
) {
let {
cursor,
signal,
filterValue
} = state;
if (cursor) {
cursor = cursor
.replace(
/^http:\/\//i,
'https://'
);
}
const response =
await fetch(
cursor ||
`https://swapi.py4e.com/api/people/?search=`,
{ signal }
);
const {
results,
next
} =
await response
.json();
return {
...state,
items: results,
cursor: next
};
}
});
return (
<ComboBox
label="Favorite Star Wars character?"
items={list.items}
onInputChange={list
.setFilterValue}
loadingState={list
.loadingState}
onLoadMoreItems={list
.loadMoreItems}
>
{(item) => (
<Item
key={item.name}
>
<Avatar src="https://cdn-icons-png.flaticon.com/512/147/147144.png?w=360" />
<Text>
{item.name}
</Text>
<Description>
Height:{' '}
{item.height}
{' '}
Weight:{' '}
{item.mass}
</Description>
</Item>
)}
</ComboBox>
);
}
Accessibility
For enhanced mobile device accessibility, the ComboBox component exchanges the default Overlay component to a Tray component. For more details on this component, please consult the Tray component docs.
In order to support internationalization, provide a localized string to the label or aria-label prop.