MultiSelectField Props
import { useState } from "react";
import { MultiSelectField } from "@servicetitan/anvil2/beta";
const ExampleComponent = () => {
const [selectedOptions, setSelectedOptions] = useState([]);
return (
<MultiSelectField
label="Search users"
loadOptions={async (searchValue) => {
const response = await fetch(`/api/users?q=${searchValue}`);
const users = await response.json();
return users.map((user) => ({
id: user.id,
label: user.name,
}));
}}
onSelectedOptionsChange={setSelectedOptions}
value={selectedOptions}
/>
);
};
The label of the multi-select field.
Function to load options. The signature depends on the lazy mode:
- Non-lazy:
(searchValue: string) => MultiSelectFieldOption[] | Promise<MultiSelectFieldOption[]>
- Page-based:
(searchValue: string, pageNumber: number, pageSize: number) => { options: MultiSelectFieldOption[], hasMore?: boolean }
- Offset-based:
(searchValue: string, offset: number, limit: number) => { options: MultiSelectFieldOption[], hasMore?: boolean }
- Group-based:
(searchValue: string, previousGroupKey: string | number | null) => { options: MultiSelectFieldGroupedOption[], hasMore?: boolean }
onSelectedOptionsChange
(options: MultiSelectFieldOption[]) => void
required
Callback fired when the selected options change. Receives the new array of selected options.
value
MultiSelectFieldOption[]
required
The currently selected options. Must be controlled state.
cache
MultiSelectFieldCacheOptions
Configuration for caching loadOptions results:
enabled — Whether caching is enabled (default: true)
maxSize — Maximum number of search values to cache before clearing (default: 50)
Custom CSS class name for the wrapper element.
Milliseconds to debounce search input before calling loadOptions.
Description text displayed below the input field.
Whether the field is disabled. When disabled, the input is still focusable but menu items cannot be selected.
How to display the options menu:
auto — Popover on desktop, dialog on mobile
popover — Always display as popover
dialog — Always display as dialog
error
ReactElement | string | boolean
Error state for the field. When true, shows error styling. When a string or ReactElement, also displays as an error message below the field.
errorAriaLive
'off' | 'assertive' | 'polite'
default:"assertive"
Controls the aria-live behavior for error messages. See MDN docs. groupToString
(groupValue: MultiSelectFieldGroupByValue) => string
Function to convert group values to display labels. Only used with grouped options. MultiSelectFieldGroupByValue is string | number.
Visually hides the label above the input while keeping it accessible to screen readers. Note: This does not affect the label displayed in the adaptive dialog view on mobile devices.
Hint text displayed below the input field.
The id of the multi-select field.
initialLoad
'auto' | 'immediate' | 'open'
default:"auto"
Controls when loadOptions is first called:
auto — Currently equivalent to immediate
immediate — Load on component mount
open — Load when dropdown is opened
lazy
'page' | 'offset' | 'group' | false
default:"false"
Enables lazy loading with the specified pagination strategy.
Custom ReactNode to render as the label above the input, overriding the default label text. The label prop is still required for accessibility purposes. Note: This does not affect the label displayed in the adaptive dialog view on mobile devices.
Configuration for lazy loading:
- Page mode:
{ pageSize?: number } (default pageSize: 20)
- Offset mode:
{ limit?: number } (default limit: 20)
- Group mode:
{}
Maximum number of chips to display before showing a “+N” indicator. Applies regardless of singleRow setting.
Placeholder text for the input field.
Content to display before the input field.
Whether the field is read-only. When read-only, the input is still focusable but menu items cannot be selected.
Whether the field is required. Shows a red asterisk (*) next to the label.
Configuration for the “Select All” option:
label — The label to display (default: “Select All”)
onClick — Callback when clicked. Parent component handles select/deselect logic.
isChecked — State: true/"checked", false/"unchecked", "indeterminate", or "loading"
When true, restricts the field to a single row height. Overflow chips collapse into a “+N” indicator.
size
'small' | 'medium' | 'large'
The size of the multi-select field.
Custom inline styles for the wrapper element.
Content to display after the input field.
MultiSelectFieldSync Props
MultiSelectFieldSync accepts all props from MultiSelectField except loadOptions, lazy, debounceMs, cache, and initialLoad, plus the following:import { useState } from "react";
import { MultiSelectFieldSync } from "@servicetitan/anvil2/beta";
const options = [
{ id: 1, label: "Option One" },
{ id: 2, label: "Option Two" },
{ id: 3, label: "Option Three" },
];
const ExampleComponent = () => {
const [selectedOptions, setSelectedOptions] = useState([]);
return (
<MultiSelectFieldSync
label="Select options"
onSelectedOptionsChange={setSelectedOptions}
options={options}
value={selectedOptions}
/>
);
};
The label of the multi-select field.
onSelectedOptionsChange
(options: MultiSelectFieldOption[]) => void
required
Callback fired when the selected options change. Receives the new array of selected options.
options
MultiSelectFieldOption[]
required
The array of options to display in the multi-select field.
value
MultiSelectFieldOption[]
required
The currently selected options. Must be controlled state.
Custom CSS class name for the wrapper element.
Description text displayed below the input field.
Whether the field is disabled. When disabled, the input is still focusable but menu items cannot be selected.
How to display the options menu.
error
ReactElement | string | boolean
Error state for the field. When true, shows error styling. When a string or ReactElement, also displays as an error message below the field.
errorAriaLive
'off' | 'assertive' | 'polite'
default:"assertive"
Controls the aria-live behavior for error messages.
filter
function | MatchSorterOptions
Custom filter configuration. Can be:
- A function:
(options: MultiSelectFieldOption[], searchValue: string) => MultiSelectFieldOption[]
- A match-sorter options object to customize the default filtering
Default: Filters by label and searchText fields using match-sorter. Visually hides the label above the input while keeping it accessible to screen readers. Note: This does not affect the label displayed in the adaptive dialog view on mobile devices.
Hint text displayed below the input field.
Custom ReactNode to render as the label above the input, overriding the default label text. The label prop is still required for accessibility purposes. Note: This does not affect the label displayed in the adaptive dialog view on mobile devices.
Maximum number of chips to display before showing a “+N” indicator.
Placeholder text for the input field.
Content to display before the input field.
Whether the field is read-only. When read-only, the input is still focusable but menu items cannot be selected.
Whether the field is required. Shows a red asterisk (*) next to the label.
selectAll
boolean | { label?: string | ((checked: boolean) => string) }
Enables the “Select All” option at the top of the list. Can be:
true — Enable with default label “Select All”
{ label: "Custom Label" } — Enable with custom static label
{ label: (checked) => checked ? "Deselect All" : "Select All" } — Enable with dynamic label based on check state
Click handling and check state are managed automatically based on comparing options with value. When true, restricts the field to a single row height. Overflow chips collapse into a “+N” indicator.
size
'small' | 'medium' | 'large'
The size of the multi-select field.
Custom inline styles for the wrapper element.
Content to display after the input field.