# Managing the tree column and expand/collapse icon

> Learn how to render the tree expand/collapse icon and manage the tree column

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

When rendering a tree, you have to use the `<TreeGrid />` component instead of `<InfiniteTable />`.

The `<TreeGrid />` component is simply an `<InfiniteTable />` component with some props removed - those don't make sense for tree scenarios.

By default no tree column is rendered.

To specify the tree column, you have to to set the [`columns.renderTreeIcon`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderTreeIcon) prop to `true` for your column of choice.

```tsx {3} title="Specifying the tree column"
const columns: Record<string, InfiniteTableColumn<FileSystemNode>> = {
  name: { 
    renderTreeIcon: true,
    field: 'name',
    header: 'Name' 
  },
  type: { field: 'type', header: 'Type' },
  size: { field: 'sizeInKB', type: 'number', header: 'Size (KB)' },
};
```
This is very similar to how you specify the [selection column for multi-select configurations](https://infinite-table.com/docs/learn/selection/row-selection.md#using-a-selection-checkbox).

**Example: Specifying the tree column**

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

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

const allColumns: Record<string, InfiniteTableColumn<FileSystemNode>> = {
  name: { field: 'name', header: 'Name' },
  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 [treeIcon, setTreeIcon] = useState<string>('name');

  const columns = useMemo(() => {
    const cols = { ...allColumns };

    cols[treeIcon] = {
      ...cols[treeIcon],
      renderTreeIcon: true,
    };

    return cols;
  }, [treeIcon]);
  return (
    <>
      <TreeDataSource nodesKey="children" primaryKey="id" data={dataSource}>
        <div
          style={{
            color: 'var(--infinite-cell-color)',
            padding: '10px',
          }}
        >
          <p>Select the tree column</p>
          <select
            value={treeIcon}
            onChange={(e) => setTreeIcon(e.target.value)}
            title="Select column to use for the tree icon"
          >
            {Object.keys(allColumns).map((key) => (
              <option value={key}>{key}</option>
            ))}
            <option value="none">No tree column</option>
          </select>
        </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: '3',
      name: 'Media',
      sizeInKB: 1000,
      type: 'folder',
      children: [
        {
          id: '30',
          name: 'Music',
          sizeInKB: 0,
          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);
};
```

## Customizing the expand/collapse icon

Using the [column.renderTreeIcon=true](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderTreeIcon) is obviously not enough to customize the expand/collapse icon.

This prop can also be a function that returns a React node.

With the default value of `true` for [`columns.renderTreeIcon`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderTreeIcon), an icon will be rendered only for parent nodes.

If you want to render an icon for all nodes, specify a function (and differentiate between parent and leaf nodes), and it will be called regardless of whether the node is a parent or a leaf.

**Example: Customizing the expand/collapse icon**

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

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

const allColumns: Record<string, InfiniteTableColumn<FileSystemNode>> = {
  name: { field: 'name', header: 'Name' },
  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 [treeIcon, setTreeIcon] = useState<string>('name');

  const columns = useMemo(() => {
    const cols = { ...allColumns };

    cols[treeIcon] = {
      ...cols[treeIcon],
      renderTreeIcon: ({ rowInfo, toggleCurrentTreeNode }) => (
        <div
          onClick={toggleCurrentTreeNode}
          style={{
            width: 24,
            fontSize: 12,
            display: 'inline-block',
            cursor: 'pointer',
          }}
        >
          {rowInfo.isParentNode ? (rowInfo.nodeExpanded ? '👇' : '👉') : '🔴'}
        </div>
      ),
    };

    return cols;
  }, [treeIcon]);
  return (
    <>
      <TreeDataSource nodesKey="children" primaryKey="id" data={dataSource}>
        <div
          style={{
            color: 'var(--infinite-cell-color)',
            padding: '10px',
          }}
        >
          <p>Select the tree column</p>
          <select
            value={treeIcon}
            onChange={(e) => setTreeIcon(e.target.value)}
            title="Select column to use for the tree icon"
          >
            {Object.keys(allColumns).map((key) => (
              <option value={key}>{key}</option>
            ))}
            <option value="no">No tree column</option>
          </select>
        </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: 108,
          type: 'file',
          extension: 'txt',
          mimeType: 'text/plain',
        },
      ],
    },
    {
      id: '3',
      name: 'Media',
      sizeInKB: 1000,
      type: 'folder',
      children: [
        {
          id: '30',
          name: 'Music',
          sizeInKB: 0,
          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);
};
```
