# Row Selection

> InfiniteTable DataGrid component supports single and multiple row selection, including checkbox column selection and lazy rows selection

Canonical page: https://infinite-table.com/docs/learn/selection/row-selection

`InfiniteTable` offers support for both single and multiple row selection. For selecting cells, see the [Cell Selection](https://infinite-table.com/docs/learn/selection/cell-selection.md) page.

```tsx title="Configure the selection mode on the DataSource component"
<DataSource selectionMode="single-row" />

// can be "single-row", "multi-row", "multi-cell" or false
```

Multiple row selection allows people to select rows just like they would in their MacOS Finder app, by clicking desired rows and using the cmd/shift keys as modifiers.

The DataGrid also offers support for **checkbox selection**, which is another easy way of interacting with grid rows, especially when grouped or nested data is used.

Row selection (both single and multiple) is driven by the [`rowSelection`](https://infinite-table.com/docs/reference/datasource-props/index.md#rowSelection) prop, which will contain **primary keys of the selected rows**.

The value or values you specify for row selection are primary keys of the rows in the DataGrid.

Row selection is defined on the `DataSource` component, so that's where you specify your [`rowSelection`](https://infinite-table.com/docs/reference/datasource-props/index.md#rowSelection) prop (or the uncontrolled version of it, namely [`defaultRowSelection`](https://infinite-table.com/docs/reference/datasource-props/index.md#defaultRowSelection) and also the callback prop of [`onRowSelectionChange`](https://infinite-table.com/docs/reference/datasource-props/index.md#onRowSelectionChange)).

You can explicitly specify the [`selectionMode`](https://infinite-table.com/docs/reference/datasource-props/index.md#selectionMode) as `"single-row"` or `"multi-row"` (or `false`) but it will generally be derived from the value of your [`rowSelection`](https://infinite-table.com/docs/reference/datasource-props/index.md#rowSelection)/[`defaultRowSelection`](https://infinite-table.com/docs/reference/infinite-table-props.md#defaultRowSelection) prop.

# Single Row Selection

This is the most basic row selection - in this case the [`rowSelection`](https://infinite-table.com/docs/reference/datasource-props/index.md#rowSelection) prop (or the uncontrolled variant [`defaultRowSelection`](https://infinite-table.com/docs/reference/datasource-props/index.md#defaultRowSelection)) will be the primary key of the selected row (a string or a number or `null` for no selection).

```ts {4}
<DataSource<DATA_TYPE>
  primaryKey="id"
  data={[...]}
  defaultRowSelection={4}
>
  <InfiniteTable {...} />
</DataTable>

```

**Example: Uncontrolled single row selection**

Single row selection example - click a row to see selection change. You can also use your keyboard - press the spacebar to select/deselect a row.

```ts
import { InfiniteTable, DataSource } from '@infinite-table/infinite-react';
import type { InfiniteTablePropColumns } from '@infinite-table/infinite-react';
import * as React from 'react';

const columns: InfiniteTablePropColumns<Developer> = {
  country: {
    field: 'country',
  },
  firstName: {
    field: 'firstName',
  },
  stack: {
    field: 'stack',
  },
  age: { field: 'age' },
  id: { field: 'id' },
  salary: {
    field: 'salary',
    type: 'number',
  },
  canDesign: { field: 'canDesign' },
};

export default function App() {
  return (
    <DataSource<Developer>
      data={dataSource}
      defaultRowSelection={3}
      primaryKey="id"
    >
      <InfiniteTable<Developer>
        debugId="default-single-row-selection-example"
        columns={columns}
        columnDefaultWidth={150}
      />
    </DataSource>
  );
}

const dataSource = () => {
  return fetch(process.env.NEXT_PUBLIC_BASE_URL + '/developers100')
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  email: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};
```

Row selection is changed when the user clicks a row. Clicking a row selects it and clicking it again keeps the row selected. For deselecting the row with the mouse use `cmd`/`ctrl` + click.

## Keybord support

You can also use your keyboard to select a row, as by default, [`keyboardSelection`](https://infinite-table.com/docs/reference/infinite-table-props.md#keyboardSelection) is `true`. Using your keyboard, navigate to the desired row and then press the spacebar to select it. Pressing the spacebar again on the selected row will deselect it.

Both `cell` and `row` [`keyboardNavigation`](https://infinite-table.com/docs/reference/infinite-table-props.md#keyboardNavigation) are available and you can use either of them to perform row selection.

## Controlled single row selection

Row selection can be used as a [controlled](https://infinite-table.com/docs/reference/datasource-props/index.md#rowSelection) or [uncontrolled](https://infinite-table.com/docs/reference/infinite-table-props.md#defaultRowSelection) property. For the controlled version, make sure you also define your [`onRowSelectionChange`](https://infinite-table.com/docs/reference/datasource-props/index.md#onRowSelectionChange) callback prop to update the selection.

**Example: Controlled single row selection**

This example uses [`onRowSelectionChange`](https://infinite-table.com/docs/reference/datasource-props/index.md#onRowSelectionChange) callback prop to update the controlled [`rowSelection`](https://infinite-table.com/docs/reference/datasource-props/index.md#rowSelection)

```ts
import { InfiniteTable, DataSource } from '@infinite-table/infinite-react';
import type { InfiniteTablePropColumns } from '@infinite-table/infinite-react';
import * as React from 'react';
import { useState } from 'react';

const columns: InfiniteTablePropColumns<Developer> = {
  country: {
    field: 'country',
  },
  firstName: {
    field: 'firstName',
  },
  stack: {
    field: 'stack',
  },
  age: { field: 'age' },
  id: { field: 'id' },
  salary: {
    field: 'salary',
    type: 'number',
  },
  canDesign: { field: 'canDesign' },
};

export default function App() {
  const [rowSelection, setRowSelection] = useState<number | null | string>(3);
  return (
    <>
      <p
        style={{
          color: 'var(--infinite-cell-color)',
          padding: 10,
        }}
      >
        Current row selection:
        <code style={{ display: 'inline-block' }}>
          <pre> {JSON.stringify(rowSelection)}.</pre>
        </code>
      </p>

      <DataSource<Developer>
        data={dataSource}
        rowSelection={rowSelection}
        onRowSelectionChange={setRowSelection}
        primaryKey="id"
      >
        <InfiniteTable<Developer>
          debugId="controlled-single-row-selection-example"
          columns={columns}
          columnDefaultWidth={150}
        />
      </DataSource>
    </>
  );
}

const dataSource = () => {
  return fetch(process.env.NEXT_PUBLIC_BASE_URL + '/developers100')
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  email: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};
```

# Multi Row Selection

You can configure multiple selection for rows so users can interact with it through clicking around or via a checkbox selection column.

## Using your mouse and keyboard to select rows

If you're using checkboxes for selection, users will be selecting rows via click or click + `cmd`/`ctrl` and `shift` keys, just like they are used to in their native Finder/Explorer applications.

### Mouse interactions

For selecting with the mouse, the following gestures are supported (we tried to exactly replicate the logic/behaviour from MacOS Finder app, so most people should find it really intuitive):

- clicking a row (with no modifier keys) will select that row, while clearing any existing selection
- click + `cmd`/`ctrl` modifier key will toggle the selection for the clicked row while keeping any other existing selection. So if the row was not selected, it's being added to the current selection, while if the row was already selected, it's being removed from the selection
- click + `shift` modifier key will perform a multi selection, starting from the last selection click where the `shift` key was not used.

**Example: Multi row selection**

Use your mouse to select multiple rows. Expect click and click + `cmd`/`ctrl`/`shift` modifier keys to behave just like they are in the MacOS Finder app.

```ts
import { InfiniteTable, DataSource } from '@infinite-table/infinite-react';
import type { InfiniteTablePropColumns } from '@infinite-table/infinite-react';
import * as React from 'react';

const columns: InfiniteTablePropColumns<Developer> = {
  country: {
    field: 'country',
  },
  firstName: {
    field: 'firstName',
  },
  stack: {
    field: 'stack',
  },
  age: { field: 'age' },
  id: { field: 'id' },
  salary: {
    field: 'salary',
    type: 'number',
  },
  canDesign: { field: 'canDesign' },
};

export default function App() {
  return (
    <DataSource<Developer>
      data={dataSource}
      selectionMode="multi-row"
      primaryKey="id"
    >
      <InfiniteTable<Developer>
        debugId="default-selection-mode-multi-row-example"
        columns={columns}
        columnDefaultWidth={150}
      />
    </DataSource>
  );
}

const dataSource = () => {
  return fetch(process.env.NEXT_PUBLIC_BASE_URL + '/developers100')
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  email: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};
```

### Keyboard interactions

By default [`keyboardSelection`](https://infinite-table.com/docs/reference/infinite-table-props.md#keyboardSelection) is enabled, so you can use the **spacebar** key to select multiple rows. Using the spacebar key is equivalent to doing a mouse click, so expect the combination of **spacebar** + `cmd`/`ctrl`/`shift` modifier keys to behave just like clicking + the same modifier keys.

**Example: Multi row selection with keyboard support**

Use spacebar + optional `cmd`/`ctrl`/`shift` modifier keys just like you would do clicking + the same modifier keys.

```ts
import { InfiniteTable, DataSource } from '@infinite-table/infinite-react';
import type { InfiniteTablePropColumns } from '@infinite-table/infinite-react';
import * as React from 'react';
import { useState } from 'react';

const columns: InfiniteTablePropColumns<Developer> = {
  country: {
    field: 'country',
  },
  firstName: {
    field: 'firstName',
  },
  stack: {
    field: 'stack',
  },
  age: { field: 'age' },
  id: { field: 'id' },
  salary: {
    field: 'salary',
    type: 'number',
  },
  canDesign: { field: 'canDesign' },
};

export default function App() {
  const [keyboardSelection, setKeyboardSelection] = useState(true);
  return (
    <>
      <div
        style={{
          color: 'var(--infinite-cell-color)',
          padding: 10,
        }}
      >
        Keyboard selection is now{' '}
        <b>{keyboardSelection ? 'enabled' : 'disabled'}</b>.
        <button
          style={{
            border: '1px solid tomato',
            display: 'block',
            padding: 5,
            marginTop: 10,
          }}
          onClick={() => setKeyboardSelection((x) => !x)}
        >
          Click to toggle keyboard selection
        </button>
      </div>
      <DataSource<Developer>
        data={dataSource}
        selectionMode="multi-row"
        primaryKey="id"
      >
        <InfiniteTable<Developer>
          debugId="default-selection-mode-multi-row-keyboard-toggle-example"
          keyboardSelection={keyboardSelection}
          columns={columns}
          columnDefaultWidth={150}
        />
      </DataSource>
    </>
  );
}

const dataSource = () => {
  return fetch(process.env.NEXT_PUBLIC_BASE_URL + '/developers100')
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  email: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};
```

For selecting all the rows in the table, you can use `cmd`/`ctrl` + `A` keyboard shortcut.

## Using a selection checkbox

Selection multiple rows is made easier when there is a checkbox column and even-more-so when there is grouping.

Configuring checkbox selection is as easy as specifying [renderSelectionCheckBox](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderSelectionCheckBox) on any of the columns in the grid. [renderSelectionCheckBox](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderSelectionCheckBox) can either be the boolean `true` or a render function that allows the customization of the selection checkbox.

```ts {8}
const columns: InfiniteTablePropColumns<Developer> = {
  id: {
    field: 'id',
    defaultWidth: 80,
  },
  country: {
    // show the selection checkbox for this column
    renderSelectionCheckBox: true,
    field: 'country',
  },
  firstName: {
    field: 'firstName',
  },
};
```

Any column can show a selection checkbox if [column.renderSelectionCheckBox](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderSelectionCheckBox) is set to `true`.

There is nothing prevening you from providing multiple checkbox columns.

**Example: Multi row selection with checkbox support**

Use the selection checkboxes to select rows. You can also use the spacebar key (+ optional shift modifier) to modify the selection

```ts
import { InfiniteTable, DataSource } from '@infinite-table/infinite-react';
import type { InfiniteTablePropColumns } from '@infinite-table/infinite-react';
import * as React from 'react';

const columns: InfiniteTablePropColumns<Developer> = {
  id: {
    field: 'id',
    renderSelectionCheckBox: true,
    defaultWidth: 80,
  },
  country: {
    field: 'country',
  },
  firstName: {
    field: 'firstName',
  },
  stack: {
    field: 'stack',
  },
  age: { field: 'age', defaultWidth: 80, type: 'number' },

  salary: {
    field: 'salary',
    type: 'number',
  },
  canDesign: { field: 'canDesign' },
};

export default function App() {
  return (
    <DataSource<Developer>
      data={dataSource}
      selectionMode="multi-row"
      primaryKey="id"
    >
      <InfiniteTable<Developer>
        debugId="default-checkbox-selection-multi-row-example"
        columns={columns}
        columnDefaultWidth={150}
      />
    </DataSource>
  );
}

const dataSource = () => {
  return fetch(process.env.NEXT_PUBLIC_BASE_URL + '/developers100')
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  email: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};
```

### Mouse interactions

The mouse interactions are the obvious ones you would expect from checkbox selection. Clicking a checkbox will toggle the selection for the correspondign row. Also, clicking the header checkbox will select/deselect all the rows in the table. The selection checkbox in the column header can be in an indeterminate state (when just some of the rows are selected), and when clicking it, it will become checked and select all rows.

You can use [renderHeaderSelectionCheckBox](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderHeaderSelectionCheckBox) for a column to customize the checkbox in the column header. If no header selection checkbox is specified, [renderSelectionCheckBox](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderSelectionCheckBox) will be used for the column header as well, just like it's used for grid rows.

### Keyboard interactions

When multi-row selection is configured to use checkboxes, you can still use your keyboard to select rows. Navigate to the desired row (you can have [keyboard navigation](https://infinite-table.com/docs/reference/infinite-table-props.md#keyboardNavigation) active for either cells or rows) and press the spacebar. If the row is not selected it will select it, otherwise it will deselect it.

The only supported modifier key when selecting a row by pressing **spacebar** is the `shift` key - it allows users to extend the selection over multiple rows, which is handy.

## Specify a `rowSelection` value

When multiple row selection is used, the [`rowSelection`](https://infinite-table.com/docs/reference/datasource-props/index.md#rowSelection) prop should be an object that can have the following shape:

```ts
const rowSelection = {
  selectedRows: [3, 6, 100, 23], // those specific rows are selected
  defaultSelection: false, // while all other rows are deselected by default
};

// or
const rowSelection = {
  deselectedRows: [3, 6, 100, 23], // those specific rows are deselected
  defaultSelection: true, // all other rows are selected
};

// or, for grouped data - this example assumes groupBy=continent,country,city

// for using this form of multi-row selection when you have grouping,
// you have to specify DataSource.useGroupKeysForMultiRowSelection = true
const rowSelection = {
  selectedRows: [
    45, // row with id 45 is selected, no matter the group it is nested in
    ['Europe', 'France'], // all rows in Europe/France are selected
    ['Asia'], // all rows in Asia are selected
  ],
  deselectedRows: [
    ['Europe', 'France', 'Paris'], // all rows in Paris are deselected
  ],
  defaultSelection: false, // all other rows are selected
};
```

As shown above, the `rowSelection.selectedRows` and `rowSelection.deselectedRows` arrays can either contain:

- primary keys of rows (which are usually strings or numbers) - any non-array value inside `rowSelection.selectedRows`/`rowSelection.deselectedRows` is considered an id/primaryKey value for a leaf row in the grouped dataset.
- arrays of group keys (can be combined with primary keys as well) - those arrays describe the path of the specified selected group. Please note that `rowSelection.selectedRows` can contain certain paths while `rowSelection.deselectedRows` can contain child paths of those paths ... or any other imaginable combination. For this kind of [`rowSelection`](https://infinite-table.com/docs/reference/datasource-props/index.md#rowSelection), you need to enable [`useGroupKeysForMultiRowSelection`](https://infinite-table.com/docs/reference/datasource-props/index.md#useGroupKeysForMultiRowSelection).

 
Row Selection only uses primary keys by default, even when you have grouped data.

For grouping however, you might want to use selection with group keys - for doing that, specify [DataSource.useGroupKeysForMultiRowSelection=true](https://infinite-table.com/docs/reference/datasource-props/index.md#useGroupKeysForMultiRowSelection).
Note that if you use selection with group keys, the selection will not be relevant/consistent when the [`groupBy`](https://infinite-table.com/docs/reference/datasource-props/index.md#groupBy) changes.

When you have both grouping and [lazy loading](https://infinite-table.com/docs/reference/datasource-props/index.md#lazyLoad), [`useGroupKeysForMultiRowSelection`](https://infinite-table.com/docs/reference/datasource-props/index.md#useGroupKeysForMultiRowSelection) must be enabled - read more about it in the note below.

When [`lazyLoad`](https://infinite-table.com/docs/reference/datasource-props/index.md#lazyLoad) is being used - this means not all available groups/rows have actually been loaded yet in the dataset - we need a way to allow you to specify that those possibly unloaded rows/groups are selected or not. In this case, the `rowSelection.selectedRows`/`rowSelection.deselectedRows` arrays should not have row primary keys as strings/numbers, but rather rows/groups specified by their full path (so [`useGroupKeysForMultiRowSelection`](https://infinite-table.com/docs/reference/datasource-props/index.md#useGroupKeysForMultiRowSelection) should be set to `true`).

```ts {6}
// this example assumes groupBy=continent,country,city
const rowSelection = {
  selectedRows: [
    // row with id 45 is selected - we need this because in the lazyLoad scenario,
    // not all parents might have been made available yet
    ['Europe','Italy', 'Rome', 45],
    ['Europe','France'], // all rows in Europe/France are selected
    ['Asia'] // all rows in Asia are selected
  ]
  deselectedRows: [
    ['Europe','Italy','Rome'] // all rows in Rome are deselected
    // but note that row with id 45 is selected, so Rome will be
    // rendered with an indeterminate selection state
  ],
  defaultSelection: false // all other rows are selected
}
```

In the example above, we know that there are 3 groups (`continent`, `country`, `city`), so any item in the array that has a 4th element is a fully specified leaf node. While lazy loading, we need this fully specified path for specific nodes, so we know which group rows to render with indeterminate selection.

### Controlled selection with checkbox column

When using the controlled [`rowSelection`](https://infinite-table.com/docs/reference/datasource-props/index.md#rowSelection), make sure to specify the [`onRowSelectionChange`](https://infinite-table.com/docs/reference/datasource-props/index.md#onRowSelectionChange) callback prop to update the selection accordingly as a result of user interaction.

**Example: Multi row checkbox selection with grouping**

This example shows how you can use multiple row selection with a predefined controlled value.

Go ahead and select some groups/rows and see the selection value adjust.

The example also shows how you can use the `InfiniteTableApi` to retrieve the actual ids of the selected rows.

```ts
import { InfiniteTable, DataSource } from '@infinite-table/infinite-react';
import type {
  InfiniteTableProps,
  InfiniteTablePropColumns,
  DataSourceProps,
  DataSourcePropRowSelection_MultiRow,
} from '@infinite-table/infinite-react';
import * as React from 'react';
import { useState } from 'react';

const columns: InfiniteTablePropColumns<Developer> = {
  country: {
    field: 'country',
  },
  firstName: {
    field: 'firstName',
    defaultHiddenWhenGroupedBy: '*',
  },
  stack: {
    field: 'stack',
    renderGroupValue: ({ value }) => `Stack: ${value || ''}`,
  },
  age: { field: 'age' },
  id: { field: 'id' },
  preferredLanguage: {
    field: 'preferredLanguage',
    renderGroupValue: ({ value }) => `Lang: ${value || ''}`,
  },
  canDesign: {
    field: 'canDesign',
    renderGroupValue: ({ value }) => `Can design: ${value || ''}`,
  },
};

const defaultGroupBy: DataSourceProps<Developer>['groupBy'] = [
  {
    field: 'canDesign',
  },
  {
    field: 'stack',
  },
  {
    field: 'preferredLanguage',
  },
];

const groupColumn: InfiniteTableProps<Developer>['groupColumn'] = {
  field: 'firstName',
  renderSelectionCheckBox: true,
  defaultWidth: 300,
};

const domProps = {
  style: {
    flex: 1,
    minHeight: 500,
  },
};

export default function App() {
  const [rowSelection, setRowSelection] =
    useState<DataSourcePropRowSelection_MultiRow>({
      selectedRows: [0, 8, 10],
      defaultSelection: false,
    });

  return (
    <div
      style={{
        display: 'flex',
        flex: 1,
        overflow: 'auto',
        color: 'var(--infinite-cell-color)',
        flexFlow: 'column',
        background: 'var(--infinite-background)',
      }}
    >
      <div
        style={{
          padding: 10,
        }}
      >
        Current row selection:
        <code
          style={{
            display: 'block',
            height: 100,
            overflow: 'auto',
            border: '1px dashed currentColor',
          }}
        >
          <pre> {JSON.stringify(rowSelection)}.</pre>
        </code>
      </div>

      <DataSource<Developer>
        data={dataSource}
        groupBy={defaultGroupBy}
        rowSelection={rowSelection}
        onRowSelectionChange={setRowSelection}
        primaryKey="id"
      >
        <InfiniteTable<Developer>
          debugId="controlled-multi-row-selection-example"
          columns={columns}
          domProps={domProps}
          groupColumn={groupColumn}
          columnDefaultWidth={150}
        />
      </DataSource>
    </div>
  );
}

const dataSource = () => {
  return fetch(process.env.NEXT_PUBLIC_BASE_URL + '/developers100')
    .then((r) => r.json())
    .then((data: Developer[]) => data);
};

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  email: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};
```

## Multi Selection with Lazy Load and Grouping

Probably the most complex use-case for multi selection (with checkbox) is the combination of grouping and lazy-loading.

In this scenario, not all groups and/or rows are loaded at a given point in time, but we need to be able to know how to render each checkbox for each group - either checked, unchecked or indeterminate, all this depending on whether all children, at any nesting levels are selected or not.

In order to make this possible, the [`rowSelection`](https://infinite-table.com/docs/reference/datasource-props/index.md#rowSelection) value will only contain arrays (and not individual primary keys) in the `selectedRows` and `deselectedRows` arrays and the DataSource will be configured with [`useGroupKeysForMultiRowSelection`](https://infinite-table.com/docs/reference/datasource-props/index.md#useGroupKeysForMultiRowSelection).

**Example: Multi row checkbox selection with lazy data and grouping**

The `DataSet` has lazy loading and grouping.

The selection uses group keys (see [`useGroupKeysForMultiRowSelection`](https://infinite-table.com/docs/reference/datasource-props/index.md#useGroupKeysForMultiRowSelection)), so it can specify as selected even rows/groups that have not been loaded yet.

Note in the example below that some of the group rows are partly selected, even if the leaf rows which are specified as selected in the [`rowSelection`](https://infinite-table.com/docs/reference/datasource-props/index.md#rowSelection) are not yet loaded.

```ts
import { InfiniteTable, DataSource } from '@infinite-table/infinite-react';
import type {
  InfiniteTablePropColumns,
  InfiniteTablePropGroupColumn,
  DataSourceData,
  DataSourcePropRowSelection_MultiRow,
  DataSourcePropGroupBy,
} from '@infinite-table/infinite-react';
import * as React from 'react';
import { useState } from 'react';

type Developer = {
  id: number;
  firstName: string;
  lastName: string;
  country: string;
  city: string;
  currency: string;
  preferredLanguage: string;
  stack: string;
  canDesign: 'yes' | 'no';
  hobby: string;
  salary: number;
  age: number;
};

const dataSource: DataSourceData<Developer> = ({
  pivotBy,
  aggregationReducers,
  groupBy,

  groupKeys = [],
}) => {
  const args = [
    pivotBy
      ? 'pivotBy=' + JSON.stringify(pivotBy.map((p) => ({ field: p.field })))
      : null,
    `groupKeys=${JSON.stringify(groupKeys)}`,
    groupBy
      ? 'groupBy=' + JSON.stringify(groupBy.map((p) => ({ field: p.field })))
      : null,
    aggregationReducers
      ? 'reducers=' +
        JSON.stringify(
          Object.keys(aggregationReducers).map((key) => ({
            field: aggregationReducers[key].field,
            id: key,
            name: aggregationReducers[key].reducer,
          })),
        )
      : null,
  ]
    .filter(Boolean)
    .join('&');
  return fetch(process.env.NEXT_PUBLIC_BASE_URL + `/developers100-sql?` + args)
    .then((r) => r.json())
    .then((data: Developer[]) => data)
    .then((data) => {
      return new Promise((resolve) => {
        setTimeout(() => {
          resolve(data);
        }, 100);
      });
    });
};

const columns: InfiniteTablePropColumns<Developer> = {
  id: { field: 'id' },

  firstName: {
    field: 'firstName',
  },

  preferredLanguage: {
    field: 'preferredLanguage',
  },
  stack: {
    field: 'stack',
  },
};

const domProps = {
  style: {
    height: '80vh',
  },
};

const groupBy: DataSourcePropGroupBy<Developer> = [
  { field: 'stack' },
  {
    field: 'preferredLanguage',
  },
  {
    field: 'canDesign',
  },
];

const groupColumn: InfiniteTablePropGroupColumn<Developer> = {
  field: 'firstName',
  defaultWidth: 250,
};

export default function GroupByExample() {
  const [rowSelection] = useState<DataSourcePropRowSelection_MultiRow>({
    defaultSelection: false,
    selectedRows: [
      ['backend', 'Java'],
      ['backend', 'JavaScript'],
      ['backend', 'CSharp', 'no', 37],
      ['backend', 'PHP', 'no', 66],
      ['frontend'],
    ],
  });

  return (
    <DataSource<Developer>
      primaryKey="id"
      data={dataSource}
      groupBy={groupBy}
      lazyLoad
      selectionMode="multi-row"
      defaultRowSelection={rowSelection}
      useGroupKeysForMultiRowSelection
    >
      <InfiniteTable<Developer>
        debugId="lazy-multi-row-selection-example"
        domProps={domProps}
        columns={columns}
        keyboardNavigation="row"
        groupRenderStrategy="single-column"
        groupColumn={groupColumn}
        columnDefaultWidth={200}
      />
    </DataSource>
  );
}
```
