Documentation Index
Fetch the complete documentation index at: https://anvil.servicetitan.com/llms.txt
Use this file to discover all available pages before exploring further.
The SelectField and MultiSelectField beta components cover many common use cases for Combobox. These components are actively receiving updates with new features and will be the recommended components for Anvil2 consumers in the future.
Implementation
Combobox and Combobox.Select Props
Combobox.SearchField and Combobox.SelectTrigger Props
Combobox.Content Props
Combobox.List Props
Combobox.Item Props
Combobox.ItemAddNew Props
Combobox.Empty Props
Common Examples
import { useState } from "react";
import { Combobox } from "@servicetitan/anvil2";
const items = [
{ id: 1, name: "First item" },
// ...more items...
];
const ExampleComponent = () => {
const [selectedItem, setSelectedItem] = useState();
return (
<Combobox
items={items}
itemToString={(item) => item ? item.name : ""}
itemToKey={(item) => item ? item.id : null}
selectedItem={selectedItem}
onChange={setSelectedItem}
filterOptions={{ keys: ["name"] }}
>
<Combobox.SearchField label="Label" />
<Combobox.Content>
{({ items }) => (
<Combobox.List>
{items.map((item, i) => (
<Combobox.Item
key={item.id}
item={item}
index={i}
>
{item.name}
</Combobox.Item>
))}
</Combobox.List>
)}
</Combobox.Content>
</Combobox>
);
Combobox components
The Anvil2 combobox is made up of a Combobox component and a few sub-components that can be used together to build simple or complex select drop-downs.
Combobox: the parent component to configure comboboxes.
Combobox.SearchField: used to create a searchable select input to use with a combobox list in a drop-down.
Combobox.Content: popover that passes an items array to its children to construct combobox items.
Combobox.List: organizes combobox items in the drop-down list.
Combobox.Item: used to construct the items within the combobox drop-down.
Combobox.Empty: optionally used to customize empty results message.
Multi-selects
To allow the user to select multiple items, set the multiple prop of the Combobox to true. In addition, props such as selectedItem will be replaced with selectedItems (note the “s” at the end) and will expect an array.Select all for multi-selects
To add a “Select All” option at the top of a multi-select combobox dropdown, use the selectAll prop. This prop accepts an object with label, onSelection, and isChecked properties. The onSelection callback is called when the user clicks the Select All option, and isChecked controls whether the checkbox appears checked.Note: The “Select All” option does not automatically select all items when the combobox is opened. It is up to the developer to handle the selection of all items when the “Select All” option is clicked. This means it is incompatible with using Combobox in an uncontrolled way.import { useState } from "react";
import { Combobox } from "@servicetitan/anvil2";
const items = [
{ id: 1, name: "First item" },
{ id: 2, name: "Second item" },
{ id: 3, name: "Third item" },
{ id: 4, name: "Fourth item" },
{ id: 5, name: "Fifth item" },
];
const ExampleComponent = () => {
const [selectedItems, setSelectedItems] = useState([]);
return (
<Combobox
multiple
items={items}
itemToString={(item) => item?.name ?? ""}
itemToKey={(item) => item?.id ?? null}
selectedItems={selectedItems}
onChange={setSelectedItems}
filterOptions={{ keys: ["name"] }}
selectAll={{
// Customize the label of the "Select All" option
label: "Select All",
// Callback function to handle the selection of all items
onSelection: () => {
// This is up to the developer to implement based on their use case
setSelectedItems((prev) =>
prev.length === items.length ? [] : items
);
},
// Control whether the "Select All" option appears checked
isChecked: selectedItems.length === items.length,
}}
>
<Combobox.SearchField label="Label" />
<Combobox.Content>
{({ items }) => (
<Combobox.List>
{items.map((item, i) => (
<Combobox.Item key={item.id} item={item} index={i}>
{item.name}
</Combobox.Item>
))}
</Combobox.List>
)}
</Combobox.Content>
</Combobox>
);
};
Building selects
The Anvil2 Combobox component is versatile due to its flexibility and robust set of sub-components. It can be used in place of the Select component from the original Anvil library to create a searchable select component.Notes for building selects
- The
itemToString prop passes an item object and expects a string return type. This is used to format the text of each combobox item within the search field input (after selecting an item) based on the provided item object type.
- The
itemToKey prop passes an item object and expects something that can be used to identify the item, such as an id. This is used for identity comparisons, so if this prop is omitted, the items will be compared by reference equality instead.
- The
filterOptions prop is used to filter the items using the match-sorter library. See their docs for more info on the options that can be provided.
- The
selectedItem/selectedItems props allow the Combobox to be used as a controlled component.
- For easily managing the selected item state, the
onChange prop accepts a setState function, which is the second item in the array that a useState function returns:
const ExampleComponent = () => {
const [selected, setSelected] = useState();
return <Combobox {...} onChange={setSelected} />
}
Compare using itemToKey or reference equality
The itemToKey prop is used to compare items using a unique identifier. By default, the Combobox component does searches and comparisons using reference equality. That means that the Combobox may not behave how you expect if your code is generating new references for your items on every render:const items = [
{ id: 1, name: "First item" },
{ id: 2, name: "Second item" },
{ id: 3, name: "Third item" },
{ id: 4, name: "Fourth item" },
{ id: 5, name: "Fifth item" },
];
const ExampleComponent = () => {
const [selected, setSelected] = useState();
// WARNING! This will create a new reference for every item on every render!
const newItems = items.map(item => { ...item, someChange: Math.random() });
return (
<Combobox
items={newItems}
>
{...}
</Combobox>
)
}
Some ways have stable references might be to perform the reference modifications outside of React’s render cycle or to use React.useMemo to memoize computations on items and only update the references when something has meaningfully changed.However, in cases like this, it’s probably more likely you’d prefer to have your items compared by a specific key in your object rather than by reference equality. And so you can do that by passing in an itemToKey function that takes an item and returns a unique key:const items = [
{ id: 1, name: "First item" },
{ id: 2, name: "Second item" },
{ id: 3, name: "Third item" },
{ id: 4, name: "Fourth item" },
{ id: 5, name: "Fifth item" },
];
const ExampleComponent = () => {
const [selected, setSelected] = useState();
const newItems = items.map(item => { ...item, someChange: Math.random() });
return (
<Combobox
items={newItems}
itemToKey={(item) => item?.id}
>
{...}
</Combobox>
)
}
Passing filtered items through the combobox components
The items array passed to the Combobox is automatically filtered based on the user typing in the search input and the keys in the filterOption prop. After filtering, it is passed through the Combobox.Content as a parameter used to generate the children.// objects in items array can have any type
const items = [
{ id: 1, label: "First item", alias: "one" },
{ id: 2, label: "Second item", alias: "two" },
{ id: 3, label: "Third item", alias: "three" },
];
const ExampleComponent = () => {
const [selectedItems, setSelectedItems] = useState([]);
return (
<Combobox
multiple
items={items}
itemToString={(item) => item?.label ?? ""}
itemToKey={(item) => item?.id ?? null}
selectedItems={selectedItems}
onChange={setSelectedItems}
/* strings in keys array should match the
searchable fields in items objects */
filterOptions={{ keys: ["label", "alias"] }}
>
<Combobox.SearchField {...searchFieldProps} />
<Combobox.Content>
{/* matches items array, with filtering */}
{({ items }) => (
<Combobox.List>
{items.map((item, index) => (
<Combobox.Item key={item.id} item={item} index={index}>
{item.label}
</Combobox.Item>
))}
</Combobox.List>
)}
</Combobox.Content>
</Combobox>
);
};
Adding a new item when there is no exact match
To allow the user to add a new item when their search returns no exact matches, use the Combobox.ItemAddNew component. This component provides an onSelection callback, and that callback will need to be used to update both your list of items and your selected item.Your onSelection callback will need to both update the list of items and update the selectedItem/selectedItems, so we recommend that you use a controlled Combobox when you need the ability to add a new item:Select-only comboboxes
By default, comboboxes include a text input for searching and filtering items. To create a select-only combobox that acts similarly to an HTML select, use the Combobox.Select and Combobox.SelectTrigger instead of Combobox and Combobox.SearchField.Disable clearing selection on select-only combobox
To remove the “x” button that clears a selection in a select-only combobox, use the disableClearSelection prop.Note: this does not work when multiple is true, due to the nature of clearing options in multi-select comboboxes.Sorting combobox options
By default, Comboboxes sort options based on the order that they are provided via the items prop.Controlled combobox
Comboboxes can be used uncontrolled by default, or can be used in a fully-controlled way by setting the selectedItem prop (or by using the selectedItems prop when used with multiple select):Default combobox selection
When using a Combobox in an uncontrolled way, Comboboxes can set an item as a default by using the defaultSelectedItem prop (or by using the defaultSelectedItems prop when used with multiple select):Selected item customization
Comboboxes with multiple select can customize the selected state of an item using the selectedItemProps prop on the Combobox.SearchField. This prop accepts a function that will pass the selected item as an parameter to allow for things like customizing the display color:Disabling combobox popovers
Comboboxes can be used to create searchable select lists without a drop-down. If disablePopover is set to true on the Combobox.Content, the list will always render. This is especially useful if the combobox exists within a custom popover.Custom combobox empty state
By default, if no items exist in the filtered combobox results, a simple “No results found” message is displayed. To customize this, use the Combobox.Empty component.Combobox virtualization example
Combobox.Content renders a function that passes down the list of filtered items as children, allowing the Combobox.List component to be used with virtualization libraries, which often require an additional wrapper component around the items being virtualized. The following example uses react-window’s FixedSizeList component for virtualizing the list of items:import { useState } from "react";
import { Combobox } from "@servicetitan/anvil2";
import { FixedSizeList as List } from "react-window";
const items = [
{ id: 1, name: "First item" },
// ...more items...
];
const ExampleComponent = () => {
const [selectedItem, setSelectedItem] = useState();
return (
<Combobox
items={items}
itemToString={(item) => (item ? item.name : "")}
itemToKey={(item) => (item ? item.id : null)}
onChange={setSelectedItem}
filterOptions={{ keys: ["name"] }}
>
<Combobox.SearchField label="Virtualization" />
<Combobox.Content>
{({ items }) => (
<List
height={Math.min(640, 34 * items.length)}
width="100%"
itemCount={items.length}
itemSize={34}
innerElementType={Combobox.List}
>
{({ index, style }) => (
<Combobox.Item
key={index}
item={items[index]}
index={index}
style={style}
>
{items[index].name}
</Combobox.Item>
)}
</List>
)}
</Combobox.Content>
</Combobox>
);
};
<Combobox
items={items}
itemToString={(item) => item?.name ?? ""}
itemToKey={(item) => item?.id ?? null}
multiple={false}
onChange={setSelectedItem}
filterOptions={{ keys: ["name"] }}
/>
Combobox and Combobox.Select Props
In addition to the props listed below, the Combobox and Combobox.Select components can accept any valid HTML div props.
-
The default
Combobox also accepts the props listed in the downshift UseComboboxProps type.
-
The
Combobox.Select component alternatively extends the downshift UseSelectProps type. Note: Props related to the input are not included for Combobox.Select, since there is no search field. (defaultInputValue, inputId, etc.)
The default selected item for single-select combobox.
The default selected item(s) for multi-select combobox.
Disables the combobox, preventing user interaction.
disableClearInputValueOnSelectItem
Prevent input from clearing when item is selected. Note: Only works with
Combobox with multiple={true}
Remove the clear button to disable user clearing the input after selecting.
Note: Only works with Combobox.Select with multiple is not true
disableCloseOnClickOutside
Prevent the drop-down popover from closing when the user clicks outside of it.
Prevent the drop-down from closing when item is selected. Note: Only works
with Combobox with multiple={true}
Name of the property on each item to group by. When provided, items are organized into labeled sections in the dropdown based on the value of this property.
groupSorter
(a: ComboboxGroupByValue, b: ComboboxGroupByValue) => number
default:"(a, b) => String(a).localeCompare(String(b))"
Function to compare two group values for sorting. Must return a negative number if a should appear before b, and a positive number if b should appear before a. ComboboxGroupByValue is string | number.
groupToString
(groupByValue: ComboboxGroupByValue) => string | null
Function to convert group values to display labels. When not provided, the raw group key value is used as the label. ComboboxGroupByValue is string | number.
onChange
(item: Item | null) => void | (items: Item[]) => void
Callback when selection changes. For single-select, receives the selected item or null. For multi-select, receives an array of selected items.
Makes the combobox read-only, allowing focus but preventing changes.
selectAll
{ label?: string; onSelection: () => void; isChecked: boolean }
Configuration for “Select All” option in multi-select comboboxes. When provided, adds a “Select All”
option at the top of the dropdown list. Only available when multiple={true}.
<Combobox.SearchField
id="combobox-1"
label="Select an item"
required={true}
moreInfo="Additional information"
prefix={{ icon: SearchIcon }}
/>
Combobox.SearchField and Combobox.SelectTrigger Props
Control opening the moreInfo tooltip.
prefix
string | { icon: IconProps }
Prefix content displayed before the input.
This function passes the selected item as a parameter and returns an object to
merge into the selected item Chip props.
<Combobox.Content disablePopover={false}>
{({ items }) => (
<Combobox.List>
{items.map((item, index) => (
<Combobox.Item key={item.id} item={item} index={index}>
{item.name}
</Combobox.Item>
))}
</Combobox.List>
)}
</Combobox.Content>
Combobox.Content Props
children
({ items, inputValue }) => ReactNode
<Combobox.List>
{items.map((item, index) => (
<Combobox.Item key={item.id} item={item} index={index}>
{item.name}
</Combobox.Item>
))}
</Combobox.List>
Combobox.List Props
<Combobox.Item index={0} item={items[0]} disabled={false}>
Item content
</Combobox.Item>
Combobox.Item Props
<Combobox.ItemAddNew onSelection={(inputValue) => console.log(inputValue)}>
Add new item
</Combobox.ItemAddNew>
Combobox.ItemAddNew Props
<Combobox.Empty>No results found</Combobox.Empty>
Combobox.Empty Props