A complete searchable multi-select field with chips, integrated label, and description.
import { SearchableChipSelectControl } from '@wordpress/ui';
LinksPermalink to this section
PropsPermalink to this section
| Name | Default | Description |
|---|---|---|
form |
Identifies the form that owns the internal input. Useful when the combobox is rendered outside the form. | |
filter |
Filter function used to match items vs input query.
Receives the source item, which is the derived value’s item when | |
disabled | false |
Whether the component should ignore user interaction. |
name |
Identifies the field when a form is submitted. | |
value |
The selected value of the combobox. Use when controlled. | |
defaultValue |
The uncontrolled selected value of the combobox when it’s initially rendered. To render a controlled combobox, use the | |
id |
The id of the component. | |
grid | false |
Whether list items are presented in a grid layout. When enabled, arrow keys navigate across rows and columns inferred from DOM rows. |
inline | false |
Whether the list is rendered inline without using the component’s own popup. Specify In a |
open |
Whether the popup is currently open. Use when controlled. | |
autoComplete |
Provides a hint to the browser for autofill. | |
readOnly | false |
Whether the user should be unable to choose a different option from the popup. |
required | false |
Whether the user must choose a value before submitting a form. |
defaultOpen | false |
Whether the popup is initially open. To render a controlled popup, use the |
limit | -1 |
The maximum number of items to display in the list. |
locale |
The locale to use for string comparison. Defaults to the user’s runtime locale. | |
onValueChange |
Event handler called when the selected value of the combobox changes. | |
onOpenChange |
Event handler called when the popup is opened or closed. | |
itemToStringValue |
When the item values are objects ( | |
isItemEqualToValue |
Custom comparison logic used to determine if a combobox item value matches the current selected value. Useful when item values are objects without matching referentially.
With a | |
inputValue |
The input value of the combobox. Use when controlled. | |
defaultInputValue |
The uncontrolled input value when initially rendered. To render a controlled input, use the | |
onInputValueChange |
Event handler called when the input value changes. | |
itemToStringLabel |
When the item values are objects ( | |
autoHighlight | false |
Whether the first matching item is highlighted automatically while filtering. |
highlightItemOnHover | true |
Whether moving the pointer over items should highlight them.
Disabling this prop allows CSS |
actionsRef |
A ref to imperative actions.
| |
openOnInputClick | true |
Whether the popup opens when clicking the input. |
filteredItems |
Filtered items to display in the list.
When provided, the list uses these items instead of filtering the | |
onOpenChangeComplete |
Event handler called after any animations complete when the popup is opened or closed. | |
loopFocus | true |
Whether to loop keyboard focus back to the input when the end of the list is reached while using the arrow keys. The first item can then be reached by pressing <kbd>ArrowDown</kbd> again from the input, or the last item can be reached by pressing <kbd>ArrowUp</kbd> from the input. The input is always included in the focus loop per ARIA Authoring Practices. When disabled, focus does not move when on the last element and the user presses <kbd>ArrowDown</kbd>, or when on the first element and the user presses <kbd>ArrowUp</kbd>. |
onItemHighlighted |
Callback fired when an item is highlighted or unhighlighted.
Receives the highlighted item value (or
| |
inputRef |
A ref to the hidden input element. | |
virtualized | false |
Whether the items are being externally virtualized. |
modal | false |
Determines if the popup enters a modal state when open.
On touch devices, a |
aria-describedby |
Identifies the element (or elements) that describes the object. | |
aria-label |
Defines a string value that labels the current element. | |
aria-labelledby |
Identifies the element (or elements) that labels the current element. | |
popupWidth | 'anchor' |
Controls how the popup width is constrained relative to its anchor. For all presets, the popup is never narrower than its anchor.
|
items |
The array of option items. When using grouped Mark a creatable action with | |
children |
A render function for custom rendering the list of matching items.
Required when | |
chipsContent |
A render function for custom rendering the selected chips. | |
emptyContent |
The custom content to use instead of the default empty state, which shows whenever there are no matching items. | |
statusContent |
Content for the list status live region. The region stays mounted. | |
searchPlaceholder |
The placeholder text to use for the search input. | |
showClearButton | true |
Whether to show the clear button to remove all selected items. |
clearButtonLabel |
The aria-label for the clear button. | |
label Required |
The accessible label. All controls must be labeled. | |
description |
The accessible description, associated using For screen reader accessibility, this should only contain plain text, and no semantics such as links. | |
details |
Additional information about the field, which unlike a normal description, can include links and other semantic elements. Do not use this prop when the content is only plain text;
use | |
hideLabelFromVision | false |
Whether to visually hide the label while keeping it accessible to screen readers. |
className |
CSS class to apply. | |
ref |
Allows getting a ref to the component instance.
Once the component unmounts, React will set | |
key |
|
ExamplesPermalink to this section
DefaultPermalink to this section
const Default = () => <SearchableChipSelectControl label="Label" description="This is a description." />;
Visually Hidden LabelPermalink to this section
const VisuallyHiddenLabel = () => <SearchableChipSelectControl hideLabelFromVision />;
With DetailsPermalink to this section
const WithDetails = () => <SearchableChipSelectControl description={undefined} details={DETAILS_EXAMPLE} />;
With Custom Search PlaceholderPermalink to this section
Use the searchPlaceholder prop to customize the search input placeholder
text. Prefer a concise label without a trailing ellipsis.
const WithCustomSearchPlaceholder = () => <SearchableChipSelectControl searchPlaceholder="Search fruit" />;
With Disabled OptionPermalink to this section
const WithDisabledOption = () => <SearchableChipSelectControl items={disabledOptionItems} defaultValue={[ disabledOptionItems[ 0 ] ]} />;
CreatablePermalink to this section
Mark a creatable action with creatable: true on an item in items.
It renders in the list footer, not the main list, when it is in the
filtered items. Handle the creation of the item in onValueChange.
const Creatable = function Template( args ) {
const {
items = ITEMS,
value: initialValue = [ ITEMS[ 0 ], ITEMS[ 1 ] ],
...restArgs
} = args;
const [ inputValue, setInputValue ] = useState( '' );
const [ value, setValue ] = useState( initialValue );
const creatableItem = {
value: '__create__',
label:
'Create new item' + ( inputValue ? `: ${ inputValue }` : '' ),
creatable: true,
};
return (
<SearchableChipSelectControl
{ ...restArgs }
items={ [ ...( items as FixtureItem[] ), creatableItem ] }
inputValue={ inputValue }
onInputValueChange={ setInputValue }
value={ value }
onValueChange={ ( values, event ) => {
if (
values.some(
( item ) => item.value === creatableItem.value
)
) {
// eslint-disable-next-line no-alert
alert( `Create new item: '${ inputValue }'` );
setValue(
values.filter(
( item ) => item.value !== creatableItem.value
)
);
} else {
setValue( values );
}
args.onValueChange?.( values, event );
} }
/>
);
};
With Custom Chips And ItemsPermalink to this section
const WithCustomChipsAndItems = () => <SearchableChipSelectControl />;
With Custom Empty ContentPermalink to this section
const WithCustomEmptyContent = () => <SearchableChipSelectControl />;
Async ItemsPermalink to this section
Loads the item list asynchronously. statusContent shows loading, then
a visually hidden result count. Pass emptyContent={ null } while
loading so Empty does not claim there are no results.
const AsyncItems = () => {
const [ loading, setLoading ] = useState( false );
const [ items, setItems ] = useState< typeof ITEMS >( [] );
const timeoutRef = useRef< ReturnType< typeof setTimeout > >();
return (
<SearchableChipSelectControl
label="Label"
description="This is a description."
items={ items }
statusContent={
loading ? (
<Stack direction="row" gap="sm" align="center">
<Spinner />
Loading…
</Stack>
) : (
<HiddenResultCount />
)
}
emptyContent={ loading ? null : undefined }
onOpenChange={ ( open ) => {
if ( ! open ) {
clearTimeout( timeoutRef.current );
return;
}
setLoading( true );
setItems( [] );
clearTimeout( timeoutRef.current );
timeoutRef.current = setTimeout( () => {
setItems( ITEMS );
setLoading( false );
}, 500 );
} } />
);
};
Without Clear ButtonPermalink to this section
const WithoutClearButton = () => <SearchableChipSelectControl />;
GroupedPermalink to this section
Options can be organized into labeled groups with
SearchableChipSelectControl.Group, SearchableChipSelectControl.GroupLabel,
and SearchableChipSelectControl.Collection. Pass an array of groups to
items (each with label and items properties), and use children to
render each group.
const Grouped = () => <SearchableChipSelectControl
defaultValue={[
GROUPED_ITEMS[ 0 ].items[ 0 ],
GROUPED_ITEMS[ 1 ].items[ 0 ],
]}
label="Fruit"
description="Choose your favorite fruits." />;
Grouped CreatablePermalink to this section
Grouped items with a creatable footer item. Include the creatable item in
items as a creatable-only group. Handle the creation of the item in
onValueChange.
const GroupedCreatable = () => {
const [ inputValue, setInputValue ] = useState( '' );
const [ value, setValue ] = useState< FixtureItem[] >( [
GROUPED_ITEMS[ 0 ].items[ 0 ],
GROUPED_ITEMS[ 1 ].items[ 0 ],
] );
const creatableItem = {
value: '__create__',
label:
'Create new item' + ( inputValue ? `: ${ inputValue }` : '' ),
creatable: true,
};
const items = [
...GROUPED_ITEMS,
{ label: '', items: [ creatableItem ] },
];
return (
<SearchableChipSelectControl
label="Fruit"
description="Choose your favorite fruits."
items={ items }
inputValue={ inputValue }
onInputValueChange={ setInputValue }
value={ value }
onValueChange={ ( values: FixtureItem[], event ) => {
if (
values.some(
( item ) => item.value === creatableItem.value
)
) {
// eslint-disable-next-line no-alert
alert( `Create new item: '${ inputValue }'` );
setValue(
values.filter(
( item ) => item.value !== creatableItem.value
)
);
} else {
setValue( values );
}
args.onValueChange?.( values, event );
} }
children={ ( group: FixtureGroup ) => (
<SearchableChipSelectControl.Group
key={ group.label }
items={ group.items }
>
<SearchableChipSelectControl.GroupLabel>
{ group.label }
</SearchableChipSelectControl.GroupLabel>
<SearchableChipSelectControl.Collection>
{ ( item: FixtureItem ) => (
<SearchableChipSelectControl.Item
key={ item.value }
value={ item }
>
{ item.label }
</SearchableChipSelectControl.Item>
) }
</SearchableChipSelectControl.Collection>
</SearchableChipSelectControl.Group>
) } />
);
};
Popup WidthPermalink to this section
Use popupWidth to control how the popup width is constrained relative to
its anchor.
This example uses sm, allowing the popup to extend beyond the narrow anchor width.
const PopupWidth = () => <SearchableChipSelectControl
label="Tags"
popupWidth="sm"
items={longLabelPopupItems}
value={[ longLabelPopupItems[ 0 ] ]} />;