# TreeNode

```ts
import { TreeNode } from 'cx/widgets';
```

The `TreeNode` widget renders the expand/collapse arrow, indentation, and icon for hierarchical data in a [TreeGrid](/docs/tables/tree-grid). Use it inside a column's `children` property.

```tsx
import { createModel } from "cx/data";
import { Controller, expr, KeySelection, TreeAdapter } from "cx/ui";
import { Grid, TreeNode } from "cx/widgets";

import "../../icons/lucide";

interface TreeRecord {
  id: number;
  name: string;
  $leaf?: boolean;
  $expanded?: boolean;
  $level?: number;
  $children?: TreeRecord[];
}

interface PageModel {
  data: TreeRecord[];
  selection: number;
  $record: TreeRecord;
}

const m = createModel<PageModel>();

function getFileIcon(name: string): string {
  if (name.endsWith(".tsx") || name.endsWith(".ts")) return "file-code";
  if (name.endsWith(".json")) return "file-json";
  if (name.endsWith(".md")) return "file-text";
  return "file";
}

class PageController extends Controller {
  onInit() {
    this.store.set(m.data, [
      {
        id: 1,
        name: "src",
        $leaf: false,
        $expanded: true,
        $children: [
          { id: 2, name: "index.tsx", $leaf: true },
          { id: 3, name: "App.tsx", $leaf: true },
          {
            id: 4,
            name: "components",
            $leaf: false,
            $children: [
              { id: 5, name: "Header.tsx", $leaf: true },
              { id: 6, name: "Footer.tsx", $leaf: true },
            ],
          },
        ],
      },
      { id: 7, name: "package.json", $leaf: true },
      { id: 8, name: "README.md", $leaf: true },
    ]);
  }
}

export default (
  <div controller={PageController}>
    <Grid
      records={m.data}
      mod="tree"
      style="height: 300px"
      scrollable
      dataAdapter={{ type: TreeAdapter }}
      selection={{ type: KeySelection, bind: m.selection, keyField: "id" }}
      columns={[
        {
          header: "Name",
          field: "name",
          children: (
            <TreeNode
              expanded={m.$record.$expanded}
              leaf={m.$record.$leaf}
              level={m.$record.$level}
              text={m.$record.name}
              icon={expr(m.$record, (r) =>
                r.$leaf
                  ? getFileIcon(r.name)
                  : r.$expanded
                    ? "folder-open"
                    : "folder",
              )}
            />
          ),
        },
      ]}
    />
  </div>
);

```

## Record Properties

The `TreeAdapter` manages these special properties on each record:

- `$expanded` - Whether the node is expanded
- `$leaf` - Whether the node is a leaf (no children)
- `$level` - The depth level for indentation
- `$loading` - Whether children are being loaded

## Icons

TreeNode displays icons based on the node state:
- **Folder icon** - For collapsed non-leaf nodes (default: `folder`)
- **Open folder icon** - For expanded non-leaf nodes (default: `folder-open`)
- **Item icon** - For leaf nodes (default: `file`)
- **Loading icon** - While children are loading (default: `loading`)

Override defaults using `folderIcon`, `openFolderIcon`, `itemIcon`, or `loadingIcon` properties. Set `hideIcon` to `true` to hide icons entirely.

## Configuration

| Property | Type | Description |
| -------- | ---- | ----------- |
| `expanded` | `BooleanProp` | Whether the node is expanded. |
| `leaf` | `BooleanProp` | Whether the node is a leaf node (cannot have children). |
| `level` | `NumberProp` | Depth level inside the tree, used for indentation. |
| `loading` | `BooleanProp` | Whether the node is currently loading its children. |
| `text` | `StringProp` | Text to display alongside the icon. |
| `icon` | `StringProp` | Custom icon. If not set, default folder/file icons are used. |
| `folderIcon` | `string` | Icon for folders. Default is `folder`. |
| `openFolderIcon` | `string` | Icon for open folders. Default is `folder-open`. |
| `itemIcon` | `string` | Icon for leaf nodes. Default is `file`. |
| `loadingIcon` | `string` | Icon shown while loading. Default is `loading`. |
| `hideIcon` | `boolean` | Set to `true` to hide icons. |
| `hideArrow` | `boolean` | Set to `true` to hide the expand/collapse arrow. |