> Markdown version of [Selection](https://vaadin.com/docs/v25/components/grid/selection). Section index: [llms.txt](https://vaadin.com/docs/v25/components/llms.txt)

# Grid Selection

Grid supports single and multi-select modes. Neither is enabled by default.

## <a id="single-selection-mode"></a>Single-Selection Mode

In single-selection mode, the user can select and deselect rows by clicking anywhere on the row.

**Lit** — `grid-single-selection-mode.ts`

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

@state()
private selectedItems: Person[] = [];

protected override async firstUpdated() {
  const { people } = await getPeople();
  this.items = people;
}

protected override render() {
  return html`
    <vaadin-grid
      .items="${this.items}"
      .selectedItems="${this.selectedItems}"
      @active-item-changed="${(e: GridActiveItemChangedEvent<Person>) => {
        const item = e.detail.value;
        this.selectedItems = item ? [item] : [];
      }}"
    >
      <vaadin-grid-column path="firstName"></vaadin-grid-column>
      <vaadin-grid-column path="lastName"></vaadin-grid-column>
      <vaadin-grid-column path="email"></vaadin-grid-column>
    </vaadin-grid>
  `;
}
```

**Flow** — `GridSingleSelectionMode.java`

```java
Grid<Person> grid = new Grid<>(Person.class, false);
grid.addColumn(Person::getFirstName).setHeader("First name");
grid.addColumn(Person::getLastName).setHeader("Last name");
grid.addColumn(Person::getEmail).setHeader("Email");

List<Person> people = DataService.getPeople();
grid.setItems(people);

grid.addSelectionListener(selection -> {
    Optional<Person> optionalPerson = selection.getFirstSelectedItem();
    if (optionalPerson.isPresent()) {
        // System.out.printf("Selected person: %s%n",
        // optionalPerson.get().getFullName());
    }
});
```

**React** — `grid-single-selection-mode.tsx`

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

useEffect(() => {
  getPeople().then(({ people }) => {
    items.value = people;
  });
}, []);

return (
  <Grid
    items={items.value}
    selectedItems={selectedItems.value}
    onActiveItemChanged={(e) => {
      const item = e.detail.value;
      selectedItems.value = item ? [item] : [];
    }}
  >
    <GridColumn path="firstName" />
    <GridColumn path="lastName" />
    <GridColumn path="email" />
  </Grid>
);
```

## <a id="multi-select-mode"></a>Multi-Select Mode

In multi-select mode, the user can use a checkbox column to select and deselect more than one row — not necessarily contiguous rows. Or the user can select all rows by clicking on the checkbox in the header row — and then un-check the ones they don’t want to be selected, rather than check many, individually.

**Lit** — `grid-multi-select-mode.ts`

```typescript
@customElement('grid-multi-select-mode')
export class Example extends LitElement {
  protected override createRenderRoot() {
    const root = super.createRenderRoot();
    applyTheme(root);
    return root;
  }

  @state()
  private items: Person[] = [];

  protected override async firstUpdated() {
    const { people } = await getPeople();
    this.items = people;
  }

  protected override render() {
    return html`
      <vaadin-grid .items="${this.items}">
        <vaadin-grid-selection-column></vaadin-grid-selection-column>
        <vaadin-grid-column path="firstName"></vaadin-grid-column>
        <vaadin-grid-column path="lastName"></vaadin-grid-column>
        <vaadin-grid-column path="email"></vaadin-grid-column>
      </vaadin-grid>
    `;
  }
}
```

**Flow** — `GridMultiSelectionMode.java`

```java
Grid<Person> grid = new Grid<>(Person.class, false);
grid.setSelectionMode(Grid.SelectionMode.MULTI);
grid.addColumn(Person::getFirstName).setHeader("First name");
grid.addColumn(Person::getLastName).setHeader("Last name");
grid.addColumn(Person::getEmail).setHeader("Email");

List<Person> people = DataService.getPeople();
grid.setItems(people);

grid.addSelectionListener(selection -> {
    // System.out.printf("Number of selected people: %s%n",
    // selection.getAllSelectedItems().size());
});
```

**React** — `grid-multi-select-mode.tsx`

```tsx
<Grid items={items.value}>
  <GridSelectionColumn />
  <GridColumn path="firstName" />
  <GridColumn path="lastName" />
  <GridColumn path="email" />
</Grid>
```

### <a id="range-selection"></a>Range Selection

In addition to selecting rows individually, you may want to allow users to select or deselect a range of rows using `Shift` + Click. Since there are a variety ways for range selection, Grid provides the necessary event, `item-toggle` to create your own implementation. This event provides information about the toggled row, its selection state, and whether the user was holding `Shift` during the toggle.

The example below demonstrates a possible implementation of range selection using `item-toggle`. In this implementation, the first clicked row is stored as an anchor point. When the user holds `Shift` and clicks another row, the selection state of all rows between the anchor and the newly clicked row is updated to match the clicked row’s state. The second clicked row then becomes the new anchor point for future selections.

**Lit** — `grid-range-selection.ts`

```typescript
@customElement('grid-range-selection')
export class Example extends LitElement {
  protected override createRenderRoot() {
    const root = super.createRenderRoot();
    applyTheme(root);
    return root;
  }

  @state()
  private items: Person[] = [];

  @state()
  private selectedItems: Person[] = [];

  private rangeStartItem?: Person;

  protected override async firstUpdated() {
    const { people } = await getPeople();
    this.items = people;
  }

  handleItemToggle(event: GridItemToggleEvent<Person>) {
    const { item, selected, shiftKey } = event.detail;

    // If the anchor point isn't set, set it to the current item
    this.rangeStartItem ??= item;

    if (shiftKey) {
      // Calculcate the range of items between the anchor point and
      // the current item
      const [rangeStart, rangeEnd] = [this.rangeStartItem, item]
        .map((i) => this.items.indexOf(i))
        .sort((a, b) => a - b);
      const rangeItems = this.items.slice(rangeStart, rangeEnd + 1);

      // Update the selection state of items within the range
      // based on the state of the current item
      const newSelectedItems = new Set(this.selectedItems);
      rangeItems.forEach((rangeItem) => {
        if (selected) {
          newSelectedItems.add(rangeItem);
        } else {
          newSelectedItems.delete(rangeItem);
        }
      });
      this.selectedItems = [...newSelectedItems];
    }

    // Update the anchor point to the current item
    this.rangeStartItem = item;
  }

  protected override render() {
    return html`
      <vaadin-grid
        .items="${this.items}"
        .selectedItems="${this.selectedItems}"
        @selected-items-changed="${(e: GridSelectedItemsChangedEvent<Person>) => {
          this.selectedItems = e.detail.value;
        }}"
        @item-toggle="${this.handleItemToggle}"
      >
        <vaadin-grid-selection-column></vaadin-grid-selection-column>
        <vaadin-grid-column path="firstName"></vaadin-grid-column>
        <vaadin-grid-column path="lastName"></vaadin-grid-column>
        <vaadin-grid-column path="email"></vaadin-grid-column>
      </vaadin-grid>
    `;
  }
}
```

**Flow** — `GridRangeSelection.java`

```java
Grid<Person> grid = new Grid<>(Person.class, false);
grid.addColumn(Person::getFirstName).setHeader("First name");
grid.addColumn(Person::getLastName).setHeader("Last name");
grid.addColumn(Person::getEmail).setHeader("Email");

List<Person> people = DataService.getPeople();
grid.setItems(people);

GridMultiSelectionModel<Person> selectionModel = (GridMultiSelectionModel<Person>) grid
        .setSelectionMode(Grid.SelectionMode.MULTI);

selectionModel.addClientItemToggleListener(event -> {
    Person item = event.getItem();

    // If the anchor point isn't set, set it to the current item
    if (rangeStartItem == null) {
        rangeStartItem = item;
    }

    if (event.isShiftKey()) {
        // Calculcate the range of items between the anchor
        // point and the current item
        GridListDataView<Person> dataView = grid.getListDataView();
        int rangeStart = dataView.getItemIndex(rangeStartItem).get();
        int rangeEnd = dataView.getItemIndex(item).get();
        Person[] rangeItems = dataView.getItems()
                .skip(Math.min(rangeStart, rangeEnd))
                .limit(Math.abs(rangeStart - rangeEnd) + 1)
                .toArray(Person[]::new);

        // Update the selection state of items within the range
        // based on the state of the current item
        if (event.isSelected()) {
            selectionModel.selectItems(rangeItems);
        } else {
            selectionModel.deselectItems(rangeItems);
        }
    }

    // Update the anchor point to the current item
    rangeStartItem = item;
});
```

**React** — `grid-range-selection.tsx`

```tsx
const items = useSignal<Person[]>([]);
const selectedItems = useSignal<Person[]>([]);
const rangeStartItem = useRef<Person>(null);

useEffect(() => {
  getPeople().then(({ people }) => {
    items.value = people;
  });
}, []);

const handleItemToggle = (event: GridItemToggleEvent<Person>) => {
  const { item, selected, shiftKey } = event.detail;

  // If the anchor point isn't set, set it to the current item
  rangeStartItem.current ??= item;

  if (shiftKey) {
    // Calculcate the range of items between the anchor point and
    // the current item
    const [rangeStart, rangeEnd] = [rangeStartItem.current, item]
      .map((i) => items.value.indexOf(i))
      .sort((a, b) => a - b);
    const rangeItems = items.value.slice(rangeStart, rangeEnd + 1);

    // Update the selection state of items within the range
    // based on the state of the current item
    const newSelectedItems = new Set(selectedItems.value);
    rangeItems.forEach((rangeItem) => {
      if (selected) {
        newSelectedItems.add(rangeItem);
      } else {
        newSelectedItems.delete(rangeItem);
      }
    });
    selectedItems.value = [...newSelectedItems];
  }

  // Update the anchor point to the current item
  rangeStartItem.current = item;
};

return (
  <Grid
    items={items.value}
    selectedItems={selectedItems.value}
    onSelectedItemsChanged={(event) => {
      selectedItems.value = event.detail.value;
    }}
    onItemToggle={handleItemToggle}
  >
    <GridSelectionColumn />
    <GridColumn path="firstName" />
    <GridColumn path="lastName" />
    <GridColumn path="email" />
  </Grid>
);
```

A range of rows can also be selected by dragging from one selection checkbox to another, if that’s enabled:

**Lit**

```typescript
<vaadin-grid .items="${this.items}">
  <vaadin-grid-selection-column drag-select></vaadin-grid-selection-column>
        ...
</vaadin-grid>
```

**Flow**

```java
Grid<Person> g = new Grid<>();
g.setSelectionMode(SelectionMode.MULTI);
GridMultiSelectionModel<Person> selectionModel = (GridMultiSelectionModel<Person>)g.getSelectionModel();
selectionModel.setDragSelect(true);
```

**React**

```tsx
<Grid items={items}>
  <GridSelectionColumn dragSelect />
  ...
</Grid>
```

## <a id="selection-modes-in-flow"></a>Selection Modes in Flow

Each selection mode is represented by a `GridSelectionModel`, accessible through the `getSelectionModel()` method, which can be cast that to the specific selection model type, `SingleSelectionModel` or `MultiSelectionModel`. These interfaces provide selection mode specific APIs for configuration and selection events.

To use Grid with `Binder` in Flow, you can use `asSingleSelect()` or `asMultiSelect()`, depending on the currently defined selection mode. Both methods return interfaces that implement the `HasValue` interface for use with `Binder`.

## <a id="conditional-selection"></a>Conditional Selection

Grid allows you to configure a predicate to control which rows users may select or deselect. The predicate receives an item and must return `true` to allow selection — `false` to prevent it. This doesn’t, however, prohibit programmatic selection changes.

**Lit**

```typescript
// Example predicate function that only allows selecting orders that are not complete
// Consider storing the function in a class property to avoid changing the property on each render
const isItemSelectable = (order: Order) => order.status !== 'complete';

<vaadin-grid .isItemSelectable=${isItemSelectable}>
  ...
</vaadin-grid>
```

**Flow**

```java
Grid<Order> grid = new Grid<>();
// Example predicate that only allows selecting orders that are not complete
grid.setItemSelectableProvider(order -> order.getStatus() != Order.State.COMPLETE);
```

**React**

```tsx
// Example predicate function that only allows selecting orders that are not complete
// Consider memoizing the function with useCallback to avoid changing the prop on each render
const isItemSelectable = (order: Order) => order.status !== 'complete';

<Grid isItemSelectable={isItemSelectable}>
  ...
</Grid>
```

> **Note:** In multi-select mode, the *Select All* checkbox is hidden when using conditional selection. This is because determining the state of the checkbox would require checking all items whenever the selection changes. That could be a performance issue with large data sets or when using lazy data providers.
