# TreeAdapter

```ts
import { TreeAdapter } from 'cx/ui';
```

TreeAdapter manages hierarchical data with parent-child relationships. It handles expand/collapse state and supports lazy loading of child nodes.

```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;
  $leaf?: boolean;
  $expanded?: boolean;
  $level?: number;
  $loading?: boolean;
  entries?: TreeRecord[];
}

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

const m = createModel<PageModel>();

class PageController extends Controller {
  onInit() {
    this.store.set(m.data, [
      {
        id: 1,
        name: "Documents",
        $leaf: false,
        $expanded: true,
        entries: [
          { id: 2, name: "report.pdf", $leaf: true },
          { id: 3, name: "notes.txt", $leaf: true },
        ],
      },
      {
        id: 4,
        name: "Images",
        $leaf: false,
        entries: [
          { id: 5, name: "photo.jpg", $leaf: true },
          { id: 6, name: "logo.png", $leaf: true },
        ],
      },
      { id: 7, name: "readme.md", $leaf: true },
    ]);
  }
}

export default (
  <div controller={PageController}>
    <Grid
      records={m.data}
      mod="tree"
      style="height: 300px"
      scrollable
      dataAdapter={{ type: TreeAdapter, childrenField: "entries" }}
      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}
            />
          ),
        },
      ]}
    />
  </div>
);

```

Click the arrows to expand or collapse nodes.

## Node Fields

TreeAdapter uses special fields on each record to manage tree state. Field names can be customized via configuration (defaults shown):

| Field | Type | Description |
| ----- | ---- | ----------- |
| `$children` | `array` | Child nodes. Customize with `childrenField`. |
| `$expanded` | `boolean` | Whether the node is expanded. Customize with `expandedField`. |
| `$leaf` | `boolean` | Whether the node is a leaf. Leaf nodes cannot be expanded and `load` is not called for them. Customize with `leafField`. |
| `$level` | `number` | Depth level in the tree (0 = root). |
| `$loading` | `boolean` | Whether child nodes are currently loading. Customize with `loadingField`. |
| `$loaded` | `boolean` | Whether child nodes have been loaded. Customize with `loadedField`. |

The example above uses `childrenField: "entries"` to store children in the `entries` field instead of the default `$children`.

## Lazy Loading

For large trees, load children on demand using the `onLoad` callback:

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

The `onLoad` callback should return an array of child records or a Promise that resolves to an array.

## Configuration

| Property | Type | Description |
| -------- | ---- | ----------- |
| `recordName` | `string` | Alias used to expose record data. Default is `$record`. |
| `keyField` | `string` | Field used as the unique record key. |
| `childrenField` | `string` | Field containing child nodes. Default is `$children`. |
| `expandedField` | `string` | Field indicating expansion state. Default is `$expanded`. |
| `leafField` | `string` | Field indicating if node is a leaf. Default is `$leaf`. |
| `loadingField` | `string` | Field indicating loading state. Default is `$loading`. |
| `loadedField` | `string` | Field indicating if children are loaded. Default is `$loaded`. |
| `foldersFirst` | `boolean` | Display folders before leaf nodes. Default is `true`. |
| `hideRootNodes` | `boolean` | Hide root nodes and show their children as top level. Default is `false`. |
| `restoreExpandedNodesOnLoad` | `boolean` | Restore expanded state when data is reloaded. |
| `onLoad` | `function` | Callback to load child nodes. Receives `(context, instance, node)`. Should return an array or Promise. |
| `onLoadError` | `function` | Callback when loading children fails. |
| `immutable` | `boolean` | Prevents aliased data from being written to the parent store. |
| `sealed` | `boolean` | Prevents child components from writing aliased data. |

See also: [Data Adapters](/docs/tables/data-adapters), [Tree Grid](/docs/tables/tree-grid), [TreeNode](/docs/tables/tree-node)