Skip to main content

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>
  );
};
Last modified on January 23, 2026