> Markdown version of [Multi-Select Combo Box](https://vaadin.com/docs/next/components/multi-select-combo-box). Section index: [llms.txt](https://vaadin.com/docs/next/components/llms.txt)

# Multi-Select Combo Box

Multi-Select Combo Box allows the user to choose one or more values from a filterable list of options presented in an overlay. The component [supports the same features as the regular Combo Box](https://vaadin.com/docs/next/components/combo-box.md), such as lazy loading or allowing custom typed values. This page explains how to add this component to your project and how to configure it.

**Lit** — `multi-select-combo-box-basic.ts`

```typescript
@state()
private items: Country[] = [];

protected override async firstUpdated() {
  this.items = await getCountries();
}

protected override render() {
  return html`
    <vaadin-multi-select-combo-box
      label="Countries"
      item-label-path="name"
      item-id-path="id"
      .items="${this.items}"
    ></vaadin-multi-select-combo-box>
  `;
}
```

**Lit** — `Country.ts`

```typescript
interface Country {
    name: string;
    abbreviation: string;
    id: number;
}
export default Country;
```

**Flow** — `MultiSelectComboBoxBasic.java`

```java
MultiSelectComboBox<Country> comboBox = new MultiSelectComboBox<>(
        "Countries");
comboBox.setItems(DataService.getCountries());
comboBox.setItemLabelGenerator(Country::getName);
add(comboBox);
```

**Flow** — `Country.java`

```java
public class Country {

    @Nonnull
    private String name;

    @Nonnull
    private String abbreviation;

    @Nonnull
    private Integer id;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAbbreviation() {
        return abbreviation;
    }

    public void setAbbreviation(String abbreviation) {
        this.abbreviation = abbreviation;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Override
    public int hashCode() {
        return id;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof Country)) {
            return false;
        }
        Country other = (Country) obj;
        return Objects.equals(id, other.id);
    }
}
```

**React** — `multi-select-combo-box-basic.tsx`

```tsx
const items = useSignal<Country[]>([]);

useEffect(() => {
  getCountries().then((countries) => {
    items.value = countries;
  });
}, []);

return (
  <MultiSelectComboBox
    label="Countries"
    itemLabelPath="name"
    itemIdPath="id"
    items={items.value}
    style={{ width: '300px' }}
  />
);
```

The overlay opens when the user clicks the field using a pointing device. Using the `Up` and `Down`arrow keys, or typing a character — that occurs in at least one of the options — when the field is focused also opens the popup.

## <a id="common-combo-box-features"></a>Common Combo Box Features

Multi-Select Combo Box supports the following features from the regular Combo Box. All the linked examples and code snippets can be applied by replacing the Combo Box with a Multi-Select Combo Box.

- [Custom Value Entry](https://vaadin.com/docs/next/components/combo-box.md#custom-value-entry)

- [Custom Item Presentation](https://vaadin.com/docs/next/components/combo-box.md#custom-item-presentation)

- [Auto Open](https://vaadin.com/docs/next/components/combo-box.md#auto-open)

- [Placeholder](https://vaadin.com/docs/next/components/combo-box.md#placeholder)

- [Custom Filtering](https://vaadin.com/docs/next/components/combo-box.md#custom-filtering)

- [Partial Match Behavior](https://vaadin.com/docs/next/components/combo-box.md#partial-match-behavior)

- [Popup Width](https://vaadin.com/docs/next/components/combo-box.md#popup-width), using the `--vaadin-multi-select-combo-box-overlay-width` style, instead of `--vaadin-combo-box-overlay-width`

## <a id="selection"></a>Selection

The component allows selecting multiple values, each of which is displayed as a chip inside the component. If there isn’t enough space in the component to display chips for all selected values, some values are collapsed into an overflow chip. The example below shows a Multi-Select Combo Box with multiple preselected values, some of which are collapsed into the overflow chip:

**Lit** — `multi-select-combo-box-selection.ts`

```html
<vaadin-multi-select-combo-box
  label="Countries"
  item-label-path="name"
  item-id-path="id"
  .items="${this.items}"
  .selectedItems="${this.items.slice(0, 4)}"
></vaadin-multi-select-combo-box>
```

**Lit** — `Country.ts`

```typescript
interface Country {
    name: string;
    abbreviation: string;
    id: number;
}
export default Country;
```

**Flow** — `MultiSelectComboBoxSelection.java`

```java
comboBox.select(countries.subList(0, 4));
```

**Flow** — `Country.java`

```java
public class Country {

    @Nonnull
    private String name;

    @Nonnull
    private String abbreviation;

    @Nonnull
    private Integer id;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAbbreviation() {
        return abbreviation;
    }

    public void setAbbreviation(String abbreviation) {
        this.abbreviation = abbreviation;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Override
    public int hashCode() {
        return id;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof Country)) {
            return false;
        }
        Country other = (Country) obj;
        return Objects.equals(id, other.id);
    }
}
```

**React** — `multi-select-combo-box-selection.tsx`

```tsx
return (
  <MultiSelectComboBox
    label="Countries"
    itemLabelPath="name"
    itemIdPath="id"
    items={items.value}
    selectedItems={selectedItems.value}
    style={{ width: '300px' }}
  />
);
```

When the overlay is closed, items can be removed one by one (starting with the most recently selected item) using the `Backspace` key. The first `Backspace` press moves focus to the last chip; the second press removes that chip, and the corresponding item, from the selection.

> **Note: Selection Uses equals() and hashCode()**
>
> Multi-Select Combo Box matches selected values to its items using `equals()` and `hashCode()`, not reference identity. `setValue(Set)` and `select()` preselect only items that are *equal* to those given to `setItems()`. The `Country` class used here overrides both methods (based on its `id`), which is why the preselected chips appear.
>
> This matters for entity types: JPA entities that don’t override `equals()` and `hashCode()` won’t preselect, because freshly loaded instances aren’t equal to the option instances the component holds — so the chips silently don’t appear. Either override both methods (for example, based on the entity’s ID), or select the exact item instances the component already holds.

### <a id="selection-change"></a>Selection Change

The following example demonstrates how to listen for selection changes:

Use the `selected-items-changed` event to react to the user changing the selection.

**Lit** — `multi-select-combo-box-selection-change.ts`

```typescript
@state()
private selectedCountries: Country[] = [];

private get selectedCountriesText(): string {
  return this.selectedCountries.map((country) => country.name).join(', ');
}

protected override render() {
  return html`
    <vaadin-horizontal-layout theme="spacing" style="align-items: baseline">
      <vaadin-multi-select-combo-box
        label="Countries"
        item-label-path="name"
        item-id-path="id"
        .items="${this.items}"
        .selectedItems="${this.selectedCountries}"
        @selected-items-changed="${(e: MultiSelectComboBoxSelectedItemsChangedEvent<Country>) => {
          this.selectedCountries = e.detail.value;
        }}"
      ></vaadin-multi-select-combo-box>
      <vaadin-text-area
        label="Selected Countries"
        readonly
        .value="${this.selectedCountriesText}"
      ></vaadin-text-area>
    </vaadin-horizontal-layout>
  `;
}
```

**Lit** — `Country.ts`

```typescript
interface Country {
    name: string;
    abbreviation: string;
    id: number;
}
export default Country;
```

Use `addValueChangeListener()` to be notified about the user changing the selection. Alternatively, `addSelectionChangeListener()` can be used to get more detailed information about the selection change.

**Flow** — `MultiSelectComboBoxSelectionChange.java`

```java
TextArea selectedCountries = new TextArea("Selected Countries");
selectedCountries.setReadOnly(true);

comboBox.addValueChangeListener(e -> {
    String selectedCountriesText = e.getValue().stream()
            .map(Country::getName).collect(Collectors.joining(", "));

    selectedCountries.setValue(selectedCountriesText);
});
```

**Flow** — `Country.java`

```java
public class Country {

    @Nonnull
    private String name;

    @Nonnull
    private String abbreviation;

    @Nonnull
    private Integer id;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAbbreviation() {
        return abbreviation;
    }

    public void setAbbreviation(String abbreviation) {
        this.abbreviation = abbreviation;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Override
    public int hashCode() {
        return id;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof Country)) {
            return false;
        }
        Country other = (Country) obj;
        return Objects.equals(id, other.id);
    }
}
```

**React** — `multi-select-combo-box-selection-change.tsx`

```tsx
const selectedCountries = useSignal<Country[]>([]);
const selectedCountriesText = selectedCountries.value.map((country) => country.name).join(', ');

return (
  <HorizontalLayout theme="spacing" style={{ alignItems: 'baseline' }}>
    <MultiSelectComboBox
      label="Countries"
      itemLabelPath="name"
      itemIdPath="id"
      items={items.value}
      selectedItems={selectedCountries.value}
      onSelectedItemsChanged={(event) => {
        selectedCountries.value = event.detail.value;
      }}
    />

    <TextArea label="Selected Countries" readonly value={selectedCountriesText} />
  </HorizontalLayout>
);
```

### <a id="select-all"></a>Select All (V25.4 pre-release)

The component can be configured to show a button in the overlay that selects all items matching the current filter, or deselects them if they’re all selected already. Items that don’t match the filter keep their selection state.

**Lit** — `multi-select-combo-box-select-all.ts`

```html
<vaadin-multi-select-combo-box
  label="Countries"
  item-label-path="name"
  item-id-path="id"
  .items="${this.items}"
  select-all-button-visible
  style="width: 300px"
></vaadin-multi-select-combo-box>
```

**Flow** — `MultiSelectComboBoxSelectAll.java`

```java
MultiSelectComboBox<Country> comboBox = new MultiSelectComboBox<>(
        "Countries");
comboBox.setItems(DataService.getCountries());
comboBox.setItemLabelGenerator(Country::getName);
comboBox.setSelectAllButtonVisible(true);
```

**React** — `multi-select-combo-box-select-all.tsx`

```tsx
<MultiSelectComboBox
  label="Countries"
  itemLabelPath="name"
  itemIdPath="id"
  items={items.value}
  selectAllButtonVisible
  style={{ width: '300px' }}
/>
```

In Flow, the button is only supported with in-memory data providers, and isn’t shown when using lazy loading.

In Lit and React, the button is only supported with the `items` property, and isn’t shown when using a `dataProvider`.

## <a id="read-only"></a>Read-Only

The component can be set to read-only, which prevents the user from modifying its value. A read-only Multi-Select Combo Box still allows opening the overlay, which then shows only the selected values, instead of all the options. This can be useful if selected values have been collapsed into the overflow chip.

**Lit** — `multi-select-combo-box-read-only.ts`

```html
<vaadin-multi-select-combo-box
  label="Countries"
  item-label-path="name"
  item-id-path="id"
  .items="${this.items}"
  .selectedItems="${this.items.slice(0, 4)}"
  readonly
></vaadin-multi-select-combo-box>
```

**Flow** — `MultiSelectComboBoxReadOnly.java`

```java
comboBox.setReadOnly(true);
```

**React** — `multi-select-combo-box-read-only.tsx`

```tsx
<MultiSelectComboBox
  label="Countries"
  itemLabelPath="name"
  itemIdPath="id"
  items={items.value}
  selectedItems={selectedItems.value}
  readonly
  style={{ width: '300px' }}
/>
```

## <a id="auto-expand"></a>Auto Expand

The component can be configured to auto-expand so as to accommodate chips for selected items. It’s possible to expand both horizontally (i.e., until max-width is reached) and vertically — in which case the field with chips wraps in multiple lines. These modes can be used together.

**Lit** — `multi-select-combo-box-auto-expand.ts`

```html
@state()
private items: Country[] = [];

protected override async firstUpdated() {
  this.items = await getCountries();
}

protected override render() {
  return html`
    <vaadin-multi-select-combo-box
      label="Countries"
      item-label-path="name"
      item-id-path="id"
      .items="${this.items}"
      auto-expand-horizontally
      auto-expand-vertically
      .selectedItems="${this.items.slice(0, 4)}"
    ></vaadin-multi-select-combo-box>
  `;
}
```

**Flow** — `MultiSelectComboBoxAutoExpand.java`

```java
comboBox.setAutoExpand(AutoExpandMode.BOTH);
```

**React** — `multi-select-combo-box-auto-expand.tsx`

```tsx
<MultiSelectComboBox
  label="Countries"
  itemLabelPath="name"
  itemIdPath="id"
  items={items.value}
  autoExpandHorizontally
  autoExpandVertically
  selectedItems={selectedItems.value}
/>
```

## <a id="selected-items-on-top"></a>Selected Items on Top

The component can be configured to group selected items at the top of the overlay. When the selection changes, a set of items to be shown in the top group is only updated after the overlay is closed.

**Lit** — `multi-select-combo-box-selected-items-on-top.ts`

```html
@state()
private items: Country[] = [];

protected override async firstUpdated() {
  this.items = await getCountries();
}

protected override render() {
  return html`
    <vaadin-multi-select-combo-box
      label="Countries"
      item-label-path="name"
      item-id-path="id"
      item-value-path="id"
      .items="${this.items}"
      .selectedItems="${this.items.slice(20, 23)}"
      selected-items-on-top
    ></vaadin-multi-select-combo-box>
  `;
}
```

**Flow** — `MultiSelectComboBoxSelectedItemsOnTop.java`

```java
comboBox.setSelectedItemsOnTop(true);
```

**React** — `multi-select-combo-box-selected-items-on-top.tsx`

```tsx
const items = useSignal<Country[]>([]);

useEffect(() => {
  getCountries().then((countries) => {
    items.value = countries;
  });
}, []);

const selectedItems = useComputed(() => items.value.slice(20, 23));

return (
  <MultiSelectComboBox
    label="Countries"
    itemLabelPath="name"
    itemIdPath="id"
    itemValuePath="id"
    items={items.value}
    selectedItemsOnTop
    selectedItems={selectedItems.value}
  />
);
```

## <a id="keep-filter"></a>Keep Filter

By default, the filter is cleared after selecting an item, and the overlay shows the full list of items again. The component can be configured to keep the filter instead, which makes it easier to select several items that match the same filter. The filter is still cleared when the overlay is closed.

```java
comboBox.setKeepFilter(true);
```

```html
<vaadin-multi-select-combo-box keep-filter>
```

```tsx
<MultiSelectComboBox keepFilter>
```

## <a id="collapse-chips"></a>Collapse Chips (since V25.2)

By default, the component shows as many chips for selected items as fit, collapsing the remainder into the overflow chip. The component can be configured to instead collapse all chips into the overflow chip whenever they don’t all fit. This can be useful when partial chip lists take up too much space or are visually confusing.

**Lit** — `multi-select-combo-box-collapse-chips.ts`

```html
<vaadin-multi-select-combo-box
  label="Countries"
  item-label-path="name"
  item-id-path="id"
  item-value-path="id"
  .items="${this.items}"
  ?collapse-chips="${this.collapseChips}"
  .selectedItems="${this.selectedCountries}"
  @selected-items-changed="${(e: MultiSelectComboBoxSelectedItemsChangedEvent<Country>) => {
    this.selectedCountries = e.detail.value;
  }}"
  style="width: 250px"
></vaadin-multi-select-combo-box>
```

**Flow** — `MultiSelectComboBoxCollapseChips.java`

```java
comboBox.setCollapseChips(true);

Checkbox toggle = new Checkbox("Collapse chips", true);
toggle.addValueChangeListener(
        e -> comboBox.setCollapseChips(e.getValue()));
```

**React** — `multi-select-combo-box-collapse-chips.tsx`

```tsx
<MultiSelectComboBox
  label="Countries"
  itemLabelPath="name"
  itemIdPath="id"
  itemValuePath="id"
  items={items.value}
  collapseChips={collapseChips.value}
  selectedItems={selectedCountries.value}
  onSelectedItemsChanged={(event) => {
    selectedCountries.value = event.detail.value;
  }}
  style={{ width: '250px' }}
/>
```

## <a id="item-class-names"></a>Item Class Names

See [Combo Box, Item Class Names](https://vaadin.com/docs/next/components/combo-box.md#item-class-names). In addition to items in the overlay, custom class names also apply to chips representing selected items.

**Lit** — `multi-select-combo-box-item-class-name.ts`

```typescript
@state()
private items = ['Apple', 'Banana', 'Orange', 'Pear'];

protected override render() {
  return html`
    <vaadin-multi-select-combo-box
      label="Fruit"
      .items="${this.items}"
      .selectedItems="${this.items.slice(0, 2)}"
      .itemClassNameGenerator="${this.classNameGenerator}"
    ></vaadin-multi-select-combo-box>
  `;
}

protected classNameGenerator(item: string): string {
  switch (item) {
    case 'Apple':
      return 'coral';
    case 'Banana':
      return 'gold';
    case 'Orange':
      return 'orange';
    case 'Pear':
      return 'yellowgreen';
    default:
      return '';
  }
}
```

**Flow** — `MultiSelectComboBoxItemClassName.java`

```java
MultiSelectComboBox<String> comboBox = new MultiSelectComboBox<>(
        "Fruit");
List<String> items = List.of("Apple", "Banana", "Orange", "Pear");
comboBox.setItems(items);
comboBox.setClassNameGenerator((item) -> {
    switch (item) {
    case "Apple":
        return "coral";
    case "Banana":
        return "gold";
    case "Orange":
        return "orange";
    case "Pear":
        return "yellowgreen";
    default:
        return "";
    }
});
```

**React** — `multi-select-combo-box-item-class-name.tsx`

```typescript
const items = ['Apple', 'Banana', 'Orange', 'Pear'];
const selectedItems = items.slice(0, 2);

const classNameGenerator = (item: string) => {
  switch (item) {
    case 'Apple':
      return 'coral';
    case 'Banana':
      return 'gold';
    case 'Orange':
      return 'orange';
    case 'Pear':
      return 'yellowgreen';
    default:
      return '';
  }
};

return (
  <MultiSelectComboBox
    label="Fruit"
    items={items}
    selectedItems={selectedItems}
    style={{ width: '300px' }}
    itemClassNameGenerator={classNameGenerator}
  />
);
```

`multi-select-combo-box-chip-class-name.css`

```css
/* Add this to your global CSS, for example in: */
/* src/main/resources/META-INF/resources/styles.css */

vaadin-multi-select-combo-box-chip.coral {
  background: coral;
}

vaadin-multi-select-combo-box-chip.gold {
  background: gold;
}

vaadin-multi-select-combo-box-chip.orange {
  background: orange;
}

vaadin-multi-select-combo-box-chip.yellowgreen {
  background: yellowgreen;
}
```

## <a id="internationalization-i18n"></a>Internationalization (i18n)

Multi-Select Combo Box allows localizing the following messages. Except for the texts of the [select all button](#select-all), these are only used in screen reader announcements, and can’t be observed visually.

| Message            | Default                               | Description                                                                                                                                                                                                  |
| ------------------ | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `cleared`          | "Selection cleared"                   | Announced by screen readers when the clear button is clicked.                                                                                                                                                |
| `focused`          | " focused. Press Backspace to remove" | Announced by screen readers when a chip is focused.                                                                                                                                                          |
| `selected`         | " added to selection"                 | Announced by screen readers when an item is added to the selection.                                                                                                                                          |
| `deselected`       | " removed from selection"             | Announced by screen readers when an item is removed from the selection.                                                                                                                                      |
| `total`            | "{count} items selected"              | Announced by screen readers to inform about the total number of selected items. The string must contain a `{count}` placeholder, which is replaced with the actual count of selected items by the component. |
| `selectAll`        | "Select All"                          | Text of the select all button when no filter is set.                                                                                                                                                         |
| `deselectAll`      | "Deselect All"                        | Text of the select all button when no filter is set, and all items are selected.                                                                                                                             |
| `selectFiltered`   | "Select Filtered"                     | Text of the select all button when a filter is set.                                                                                                                                                          |
| `deselectFiltered` | "Deselect Filtered"                   | Text of the select all button when a filter is set, and all items matching the filter are selected.                                                                                                          |

The following example demonstrates how to localize the component’s messages into German:

**Lit** — `multi-select-combo-box-i18n.ts`

```typescript
private i18n: MultiSelectComboBoxI18n = {
  cleared: 'Alle Einträge entfernt',
  focused: ' ausgewählt. Drücke Rücktaste zum Entfernen',
  selected: ' hinzugefügt',
  deselected: ' entfernt',
  total: '{count} Einträge ausgewählt',
  selectAll: 'Alle auswählen',
  deselectAll: 'Alle abwählen',
  selectFiltered: 'Gefilterte auswählen',
  deselectFiltered: 'Gefilterte abwählen',
};

protected override render() {
  return html`
    <vaadin-multi-select-combo-box
      label="Länder"
      item-label-path="name"
      item-id-path="id"
      .items="${this.items}"
      .i18n="${this.i18n}"
      select-all-button-visible
    ></vaadin-multi-select-combo-box>
  `;
}
```

**Flow** — `MultiSelectComboBoxI18nDemo.java`

```java
MultiSelectComboBoxI18n i18n = new MultiSelectComboBoxI18n();
i18n.setCleared("Alle Einträge entfernt");
i18n.setFocused(" ausgewählt. Drücke Rücktaste zum Entfernen");
i18n.setSelected(" hinzugefügt");
i18n.setDeselected(" entfernt");
i18n.setTotal("{count} Einträge ausgewählt");
i18n.setSelectAll("Alle auswählen");
i18n.setDeselectAll("Alle abwählen");
i18n.setSelectFiltered("Gefilterte auswählen");
i18n.setDeselectFiltered("Gefilterte abwählen");

comboBox.setI18n(i18n);
```

**React** — `multi-select-combo-box-i18n.tsx`

```tsx
const i18n = {
  cleared: 'Alle Einträge entfernt',
  focused: ' ausgewählt. Drücke Rücktaste zum Entfernen',
  selected: ' hinzugefügt',
  deselected: ' entfernt',
  total: '{count} Einträge ausgewählt',
  selectAll: 'Alle auswählen',
  deselectAll: 'Alle abwählen',
  selectFiltered: 'Gefilterte auswählen',
  deselectFiltered: 'Gefilterte abwählen',
};

return (
  <MultiSelectComboBox
    label="Länder"
    itemLabelPath="name"
    itemIdPath="id"
    items={items.value}
    i18n={i18n}
    selectAllButtonVisible
    style={{ width: '300px' }}
  />
);
```

## <a id="best-practices"></a>Best Practices

Multi-Select Combo Box supports lazy loading for large datasets. It reduces the initial load time, and consumes less bandwidth and resources.

For consistency, the default width of the Multi-Select Combo Box matches that of other input fields. You should increase the width of the component when using items with long labels, or if you expect users to select several items, to avoid collapsing selected items into the overflow chip.

Example of Increasing Component Width

```html
<vaadin-multi-select-combo-box
  style="width: 300px"
></vaadin-multi-select-combo-box>
```

```Java
comboBox.setWidth("300px");
```

```tsx
<MultiSelectComboBox style={{ width: '300px' }} />
```

## <a id="related-components"></a>Related Components

| Component                                                             | Usage Recommendations                                                                     |
| --------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- |
| [Combo Box](https://vaadin.com/docs/next/components/combo-box.md)     | Same feature set, but for selecting a single value.                                       |
| [List Box](https://vaadin.com/docs/next/components/list-box.md)       | Scrollable inline list of options. Supports single and multi-select.                      |
| [Checkbox Group](https://vaadin.com/docs/next/components/checkbox.md) | Serves the same purpose in a more user-friendly format, but takes up more vertical space. |
