# TreeGrid

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



A TreeGrid is a Grid with hierarchical data displayed using the `TreeAdapter` and `TreeNode` components. It supports lazy loading, expand/collapse, and all standard Grid features.

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

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

interface TreeRecord {
  id: number;
  name: string;
  phone: string;
  city: string;
  $leaf?: boolean;
  $expanded?: boolean;
  $level?: number;
  $loading?: boolean;
}

interface PageModel {
  data: TreeRecord[];
  selection: Record<number, boolean>;
  $record: TreeRecord;
}

const m = createModel<PageModel>();

class PageController extends Controller {
  idSeq = 0;

  onInit() {
    this.store.set(m.data, this.generateRecords());
  }

  generateRecords(node?: TreeRecord): TreeRecord[] | undefined {
    if (!node || (node.$level ?? 0) < 3) {
      const names = [
        "Alice Johnson",
        "Bob Smith",
        "Carol White",
        "David Brown",
        "Eva Green",
      ];
      const cities = [
        "New York",
        "Los Angeles",
        "Chicago",
        "Houston",
        "Phoenix",
      ];
      return Array.from({ length: 5 }, (_, i) => ({
        id: ++this.idSeq,
        name: names[i % 5],
        phone: `555-${String(1000 + this.idSeq).slice(-4)}`,
        city: cities[i % 5],
        $leaf: Math.random() > 0.5,
      }));
    }
  }
}

export default (
  <div controller={PageController}>
    <Grid
      records={m.data}
      mod="tree"
      style="height: 400px"
      scrollable
      dataAdapter={{
        type: TreeAdapter,
        onLoad: (context, instance, node) =>
          instance.getControllerByType(PageController).generateRecords(node),
      }}
      selection={{ type: KeySelection, bind: m.selection, keyField: "id" }}
      columns={[
        {
          header: "Name",
          field: "name",
          sortable: true,
          children: (
            <TreeNode
              expanded={m.$record.$expanded}
              leaf={m.$record.$leaf}
              level={m.$record.$level}
              loading={m.$record.$loading}
              text={m.$record.name}
            />
          ),
        },
        { header: "Phone", field: "phone" },
        { header: "City", field: "city", sortable: true },
      ]}
    />
  </div>
);

```

Click the arrows to expand/collapse nodes. The tree loads child nodes lazily when expanded.

## TreeAdapter

To display hierarchical data, configure the `dataAdapter` property with `TreeAdapter`:

```tsx
<Grid
  records={m.data}
  mod="tree"
  dataAdapter={{ type: TreeAdapter }}
  columns={columns}
/>
```

If your data is already a tree structure with children nested in each record, that's all you need. The `TreeAdapter` will flatten the tree for display and manage expand/collapse state.

For lazy loading, implement the `onLoad` callback to fetch children when a node is expanded:

```tsx
dataAdapter={{
  type: TreeAdapter,
  onLoad: (context, instance, node) =>
    instance.getControllerByType(PageController).loadChildren(node),
}}
```

Return an array of child records or a Promise that resolves to an array.

## TreeNode

Use [TreeNode](/docs/tables/tree-node) in the column's `children` to render the expand/collapse arrow and indentation:

```tsx
{
  header: "Name",
  field: "name",
  children: (
    <TreeNode
      expanded={m.$record.$expanded}
      leaf={m.$record.$leaf}
      level={m.$record.$level}
      loading={m.$record.$loading}
      text={m.$record.name}
    />
  )
}
```

The `TreeAdapter` manages special properties on each record (`$expanded`, `$leaf`, `$level`, `$loading`) that TreeNode uses for rendering.

See also: [TreeNode](/docs/tables/tree-node), [Stateful TreeGrid](/docs/tables/stateful-tree-grid), [Searching Tree Grids](/docs/tables/searching-tree-grids)