# Using & rendering tree icons

> Learn how to customize the tree icons

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

To make a column render the tree icon, you have to set [`columns.renderTreeIcon`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderTreeIcon) to `true`. This will cause the default tree icon to be rendered for non-leaf nodes.

```tsx {4} title="Specifying the tree icon for a column"
const columns: Record<string, InfiniteTableColumn<FileSystemNode>> = {
  name: {
    field: 'name',
    renderTreeIcon: true,
  },
  type: { field: 'type' },
  extension: { field: 'extension' },
  size: { field: 'sizeInKB', type: 'number' },
};
```

If you don't have [`columns.renderTreeIcon`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderTreeIcon) set, there will be no tree column to render the tree icon.

**Example: Tree icon rendering**

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

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

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

export default function App() {
  return (
    <TreeDataSource nodesKey="children" primaryKey="id" data={dataSource}>
      <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',
            },
            {
              id: '101',
              name: 'Vacation.docx',
              sizeInKB: 120,
              type: 'file',
              extension: 'docx',
            },
            {
              id: '102',
              name: 'CV.pdf',
              sizeInKB: 108,
              type: 'file',
              extension: 'pdf',
            },
          ],
        },
      ],
    },
    {
      id: '2',
      name: 'Desktop',
      sizeInKB: 1000,
      type: 'folder',
      children: [
        {
          id: '20',
          name: 'unknown.txt',
          sizeInKB: 100,
          type: 'file',
          extension: 'txt',
        },
      ],
    },
    {
      id: '3',
      name: 'Media',
      sizeInKB: 1000,
      type: 'folder',
      children: [
        {
          id: '30',
          name: 'Music - empty',
          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',
            },
          ],
        },
      ],
    },
  ];
  return Promise.resolve(nodes);
};
```

## Customizing the tree icon

There are multiple ways to customize the tree icon.

First, you can very easily change the color of the icon. The color of the icon is controlled by the `--infinite-expand-collapse-icon-color` CSS variable, and defaults to  `--infinite-accent-color`, but you can also set it to any other color you want.

```css title="Changing the color of the tree icon"
.Infinite {
  --infinite-expand-collapse-icon-color: #6f6f6f;
}
```

**Example: Customizing the tree icon color**

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

const domProps: InfiniteTableProps<FileSystemNode>['domProps'] = {
  style: {
    // specify it here or in your CSS file
    '--infinite-expand-collapse-icon-color': '#6f6f6f',
  } as CSSProperties,
};

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

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

export default function App() {
  return (
    <TreeDataSource nodesKey="children" primaryKey="id" data={dataSource}>
      <TreeGrid columns={columns} domProps={domProps} />
    </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',
            },
            {
              id: '101',
              name: 'Vacation.docx',
              sizeInKB: 120,
              type: 'file',
              extension: 'docx',
            },
            {
              id: '102',
              name: 'CV.pdf',
              sizeInKB: 108,
              type: 'file',
              extension: 'pdf',
            },
          ],
        },
      ],
    },
    {
      id: '2',
      name: 'Desktop',
      sizeInKB: 1000,
      type: 'folder',
      children: [
        {
          id: '20',
          name: 'unknown.txt',
          sizeInKB: 100,
          type: 'file',
          extension: 'txt',
        },
      ],
    },
    {
      id: '3',
      name: 'Media',
      sizeInKB: 1000,
      type: 'folder',
      children: [
        {
          id: '30',
          name: 'Music - empty',
          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',
            },
          ],
        },
      ],
    },
  ];
  return Promise.resolve(nodes);
};
```

If you want to go further, use a function for the `column.renderTreeIcon` property - the next section will go into more detail on this.

## Rendering a custom tree icon for both parent and leaf nodes

When [`columns.renderTreeIcon`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderTreeIcon) is `true`, the tree icon will be rendered only for parent nodes.

In your implementation of the `renderTreeIcon` function, you'll use the `rowInfo.nodeExpanded` property.

Note that the property is only available for parent nodes, so you'll first have to use the `rowInfo.isParentNode` property as a TS discriminator to check if the node is a parent node.

```tsx title="Checking if the node is a parent node"
const renderTreeIcon = ({ rowInfo }) => {
  if (!rowInfo.isParentNode) {
    // rowInfo.nodeExpanded not available here
    return <FileIcon />;
  }
  
  // it's now OK for TS to use rowInfo.nodeExpanded
  return  <FolderIcon open={rowInfo.nodeExpanded} onClick={toggleCurrentTreeNode} />
};
```

However when you specify a function, it will be called for both parent and leaf nodes (if you don't want an icon for leaf nodes, simply return `null`).

This gives you maximum flexibility to icons. A common example is a file explorer, where you might want to render icons not only for folders, but also for files.

**Example: Rendering a custom tree icon for both parent and leaf nodes**

This example renders a custom tree icon and uses the `toggleCurrentTreeNode` function to toggle the node state when Clicked. `toggleCurrentTreeNode` is a property of the argument passed to the `renderTreeIcon` function.

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

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

const renderTreeIcon: InfiniteTableColumn<FileSystemNode>['renderTreeIcon'] = ({
  rowInfo,
  toggleCurrentTreeNode,
}) => {
  return rowInfo.isParentNode ? (
    <FolderIcon open={rowInfo.nodeExpanded} onClick={toggleCurrentTreeNode} />
  ) : (
    <FileIcon />
  );
};

const svgStyle: CSSProperties = {
  verticalAlign: 'middle',
  position: 'relative',
  top: '-1px',
  marginInline: '5px',
};

const FileIcon = () => (
  <svg
    xmlns="http://www.w3.org/2000/svg"
    height="20px"
    viewBox="0 -960 960 960"
    width="20px"
    fill="currentColor"
    style={svgStyle}
  >
    <path d="M240-80q-33 0-56.5-23.5T160-160v-640q0-33 23.5-56.5T240-880h320l240 240v480q0 33-23.5 56.5T720-80H240Zm280-520v-200H240v640h480v-440H520ZM240-800v200-200 640-640Z" />
  </svg>
);

const FolderIcon = ({
  onClick,
  open,
}: {
  onClick: () => void;
  open: boolean;
}) => {
  return (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      height="20px"
      viewBox="0 -960 960 960"
      width="20px"
      fill="currentColor"
      onClick={onClick}
      style={{ ...svgStyle, cursor: 'pointer' }}
    >
      {open ? (
        <path d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h240l80 80h320q33 0 56.5 23.5T880-640H447l-80-80H160v480l96-320h684L837-217q-8 26-29.5 41.5T760-160H160Zm84-80h516l72-240H316l-72 240Zm0 0 72-240-72 240Zm-84-400v-80 80Z" />
      ) : (
        <path d="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h240l80 80h320q33 0 56.5 23.5T880-640v400q0 33-23.5 56.5T800-160H160Zm0-80h640v-400H447l-80-80H160v480Zm0 0v-480 480Z" />
      )}
    </svg>
  );
};

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

export default function App() {
  return (
    <TreeDataSource nodesKey="children" primaryKey="id" data={dataSource}>
      <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',
            },
            {
              id: '101',
              name: 'Vacation.docx',
              sizeInKB: 120,
              type: 'file',
              extension: 'docx',
            },
            {
              id: '102',
              name: 'CV.pdf',
              sizeInKB: 108,
              type: 'file',
              extension: 'pdf',
            },
          ],
        },
      ],
    },
    {
      id: '2',
      name: 'Desktop',
      sizeInKB: 1000,
      type: 'folder',
      children: [
        {
          id: '20',
          name: 'unknown.txt',
          sizeInKB: 100,
          type: 'file',
          extension: 'txt',
        },
      ],
    },
    {
      id: '3',
      name: 'Media',
      sizeInKB: 1000,
      type: 'folder',
      children: [
        {
          id: '30',
          name: 'Music - empty',
          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',
            },
          ],
        },
      ],
    },
  ];
  return Promise.resolve(nodes);
};
```

If you implement a custom [`columns.renderTreeIcon`](https://infinite-table.com/docs/reference/infinite-table-props.md#columns.renderTreeIcon) function for your column, you can still use the default tree icon.

Use `renderBag.treeIcon` property in the JSX you return (the `renderBag` is available as a property of the `cellContext` argument of the `renderTreeIcon` function).
