# Using tree selection

> Learn how to leverage the tree and specify the tree selection state

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

When using a tree grid, a common use-case is to allow users to select nodes, both parent and child nodes.

The `<TreeDataSource>` component allows you to specify an initial tree selection, via the `defaultTreeSelection` prop.

When using [`defaultTreeSelection`](https://infinite-table.com/docs/reference/datasource-props/index.md#defaultTreeSelection) or its controlled counterpart [`treeSelection`](https://infinite-table.com/docs/reference/datasource-props/index.md#treeSelection), if no [`selectionMode`](https://infinite-table.com/docs/reference/datasource-props/index.md#selectionMode) is specified, the selection mode will default to `"multi-row"`.

If you enable selection, don't forget to specify which column should render a selection checkbox, by using [renderSelectionCheckBox=true](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderSelectionCheckBox).

```tsx title="Example of tree selection value with default selection set to false"
import type { TreeSelectionValue } from '@infinite-table/infinite-react';

// Default selection is false, with some selected node paths: ['1'] and ['2', '20']
// however, node ['1', '10'] is deselected
const treeSelectioDefaultDeselected: TreeSelectionValue = {
  defaultSelection: false,
  selectedPaths: [['1'], ['2', '20']],
  deselectedPaths: [['1', '10']],
};

```

```tsx title="Example of tree selection value with default selection set to true"
// Default selection is true, with some deselected node paths: ['2'] and ['3']
// however, inside ['3'], we have a selected node ['3','30','301']
const treeSelectionDefaultSelected: TreeSelectionValue = {
  defaultSelection: true,
  deselectedPaths: [['2'], ['3']],
  selectedPaths: [['3','30','301']],
};
```

**Example: Using default tree selection**

```tsx
import {
  DataSourceApi,
  InfiniteTableColumn,
  TreeDataSource,
  TreeGrid,
  TreeSelectionValue,
} from '@infinite-table/infinite-react';
import { useState } from 'react';

type FileSystemNode = {
  id: string;
  name: string;
  type: 'folder' | 'file';
  extension?: string;
  mimeType?: string;
  sizeInKB: number;
  children?: FileSystemNode[];
};

const columns: Record<string, InfiniteTableColumn<FileSystemNode>> = {
  name: {
    field: 'name',
    header: 'Name',
    renderTreeIcon: true,
    renderSelectionCheckBox: true,
  },
  type: { field: 'type', header: 'Type' },
  extension: { field: 'extension', header: 'Extension' },
  mimeType: { field: 'mimeType', header: 'Mime Type' },
  size: { field: 'sizeInKB', type: 'number', header: 'Size (KB)' },
};

const defaultTreeSelection: TreeSelectionValue = {
  defaultSelection: true,
  deselectedPaths: [
    ['1', '10'],
    ['3', '31'],
  ],
  selectedPaths: [['3']],
};

export default function App() {
  const [dataSourceApi, setDataSourceApi] =
    useState<DataSourceApi<FileSystemNode> | null>();

  return (
    <>
      <TreeDataSource
        onReady={setDataSourceApi}
        nodesKey="children"
        primaryKey="id"
        data={dataSource}
        defaultTreeSelection={defaultTreeSelection}
      >
        <div
          style={{
            color: 'var(--infinite-cell-color)',
            padding: 10,
            display: 'flex',
            gap: 10,
          }}
        >
          <button
            onClick={() => {
              dataSourceApi!.treeApi.selectAll();
            }}
          >
            Select all
          </button>
          <button
            onClick={() => {
              dataSourceApi!.treeApi.deselectAll();
            }}
          >
            Deselect all
          </button>
        </div>

        <TreeGrid columns={columns} />
      </TreeDataSource>
    </>
  );
}

const dataSource = () => {
  const nodes: FileSystemNode[] = [
    {
      id: '1',
      name: 'Documents',
      sizeInKB: 1200,
      type: 'folder',
      children: [
        {
          id: '10',
          name: 'Private',
          sizeInKB: 100,
          type: 'folder',
          children: [
            {
              id: '100',
              name: 'Report.docx',
              sizeInKB: 210,
              type: 'file',
              extension: 'docx',
              mimeType: 'application/msword',
            },
            {
              id: '101',
              name: 'Vacation.docx',
              sizeInKB: 120,
              type: 'file',
              extension: 'docx',
              mimeType: 'application/msword',
            },
            {
              id: '102',
              name: 'CV.pdf',
              sizeInKB: 108,
              type: 'file',
              extension: 'pdf',
              mimeType: 'application/pdf',
            },
          ],
        },
      ],
    },
    {
      id: '2',
      name: 'Desktop',
      sizeInKB: 1000,
      type: 'folder',
      children: [
        {
          id: '20',
          name: 'unknown.txt',
          sizeInKB: 100,
          type: 'file',
        },
      ],
    },
    {
      id: '3',
      name: 'Media',
      sizeInKB: 1000,
      type: 'folder',
      children: [
        {
          id: '31',
          name: 'Videos',
          sizeInKB: 5400,
          type: 'folder',
          children: [
            {
              id: '310',
              name: 'Vacation.mp4',
              sizeInKB: 108,
              type: 'file',
              extension: 'mp4',
              mimeType: 'video/mp4',
            },
          ],
        },
      ],
    },
  ];
  return Promise.resolve(nodes);
};
```

When using [uncontrolled tree selection](https://infinite-table.com/docs/reference/datasource-props/index.md#defaultTreeSelection), the `<TreeDataSource />` will manage the selection state internally, and will update it as a result of user actions. If you want to change the selection, you can use [the Tree API](https://infinite-table.com/docs/reference/tree-api/index.md) to do so: [`selectNode`](https://infinite-table.com/docs/reference/tree-api/index.md#selectNode), [`deselectNode`](https://infinite-table.com/docs/reference/tree-api/index.md#deselectNode), [`selectAll`](https://infinite-table.com/docs/reference/tree-api/index.md#selectAll), [`deselectAll`](https://infinite-table.com/docs/reference/tree-api/index.md#deselectAll), etc.

## Reacting to user actions

To listen to selection changes, you can use the [`onTreeSelectionChange`](https://infinite-table.com/docs/reference/datasource-props/index.md#onTreeSelectionChange) callback.

This callback is called both when the user interacts with the grid, and when you use the [Tree API](https://infinite-table.com/docs/reference/tree-api/index.md) to change the selection.

## Using controlled tree selection

When using the controlled [`treeSelection`](https://infinite-table.com/docs/reference/datasource-props/index.md#treeSelection) prop, you have to make sure you update the tree selection via [`onTreeSelectionChange`](https://infinite-table.com/docs/reference/datasource-props/index.md#onTreeSelectionChange).

Controlled tree selection also gives you a more declarative way to manage the selection state.
You no longer have to call [Tree API](https://infinite-table.com/docs/reference/tree-api/index.md) methods to change the selection. Simply pass a new tree selection state object to the [`treeSelection`](https://infinite-table.com/docs/reference/datasource-props/index.md#treeSelection) prop and the tree grid will be updated accordingly.

For example, if you want to select all nodes, set the [`treeSelection`](https://infinite-table.com/docs/reference/datasource-props/index.md#treeSelection) prop to:

```tsx title="Tree selection value to show all nodes as selected"
{
  defaultSelection: true,
  deselectedPaths: [],
}
```

For deselecting all nodes, the value should be:

```tsx title="All nodes as deselected"
{
  defaultSelection: false,
  selectedPaths: [],
}
```

Using controlled tree selection also gives you an easy way to restore a previously saved tree selection at any point in time.

**Example: Using controlled tree selection**

```tsx
import {
  InfiniteTableColumn,
  TreeDataSource,
  TreeGrid,
  TreeSelectionValue,
} from '@infinite-table/infinite-react';
import { useState } from 'react';

type FileSystemNode = {
  id: string;
  name: string;
  type: 'folder' | 'file';
  extension?: string;
  mimeType?: string;
  sizeInKB: number;
  children?: FileSystemNode[];
};

const columns: Record<string, InfiniteTableColumn<FileSystemNode>> = {
  name: {
    field: 'name',
    header: 'Name',
    renderTreeIcon: true,
    renderSelectionCheckBox: true,
  },
  type: { field: 'type', header: 'Type' },
  extension: { field: 'extension', header: 'Extension' },
  mimeType: { field: 'mimeType', header: 'Mime Type' },
  size: { field: 'sizeInKB', type: 'number', header: 'Size (KB)' },
};

export default function App() {
  const [treeSelection, setTreeSelection] = useState<TreeSelectionValue>({
    defaultSelection: false,
    selectedPaths: [['1', '10'], ['3']],
  });

  return (
    <>
      <TreeDataSource
        nodesKey="children"
        primaryKey="id"
        data={dataSource}
        treeSelection={treeSelection}
        onTreeSelectionChange={setTreeSelection}
      >
        <div
          style={{
            color: 'var(--infinite-cell-color)',
            padding: '10px',
          }}
        >
          <button
            onClick={() => {
              setTreeSelection({
                defaultSelection: false,
                selectedPaths: [],
              });
            }}
          >
            Deselect all
          </button>
          <button
            onClick={() => {
              setTreeSelection({
                defaultSelection: true,
                deselectedPaths: [],
              });
            }}
          >
            Select all
          </button>
          <div
            style={{
              overflow: 'auto',
              height: '10vh',
              minHeight: '200px',
            }}
          >
            Current tree selection:
            <pre>{JSON.stringify(treeSelection, null, 2)}</pre>
          </div>
        </div>

        <TreeGrid columns={columns} />
      </TreeDataSource>
    </>
  );
}

const dataSource = () => {
  const nodes: FileSystemNode[] = [
    {
      id: '1',
      name: 'Documents',
      sizeInKB: 1200,
      type: 'folder',
      children: [
        {
          id: '10',
          name: 'Private',
          sizeInKB: 100,
          type: 'folder',
          children: [
            {
              id: '100',
              name: 'Report.docx',
              sizeInKB: 210,
              type: 'file',
              extension: 'docx',
              mimeType: 'application/msword',
            },
            {
              id: '101',
              name: 'Vacation.docx',
              sizeInKB: 120,
              type: 'file',
              extension: 'docx',
              mimeType: 'application/msword',
            },
            {
              id: '102',
              name: 'CV.pdf',
              sizeInKB: 108,
              type: 'file',
              extension: 'pdf',
              mimeType: 'application/pdf',
            },
          ],
        },
      ],
    },
    {
      id: '2',
      name: 'Desktop',
      sizeInKB: 1000,
      type: 'folder',
      children: [
        {
          id: '20',
          name: 'unknown.txt',
          sizeInKB: 100,
          type: 'file',
        },
      ],
    },
    {
      id: '3',
      name: 'Media',
      sizeInKB: 1000,
      type: 'folder',
      children: [
        {
          id: '31',
          name: 'Videos',
          sizeInKB: 5400,
          type: 'folder',
          children: [
            {
              id: '310',
              name: 'Vacation.mp4',
              sizeInKB: 108,
              type: 'file',
              extension: 'mp4',
              mimeType: 'video/mp4',
            },
          ],
        },
      ],
    },
  ];
  return Promise.resolve(nodes);
};
```
