---
title: Treegrid
description: Treegrids combine tree hierarchy with tabular columns, such as file explorers with size and date columns, or nested budgets.
---

import { meta as TreegridMeta, Default } from '@/content/stories/treegrid';
import Canvas from '@/components/component-docs/canvas';
import Demo from '@/components/component-docs/demo';

export const config = {
	dependencies: {
		components: [Canvas, Demo],
		scripts: [
			'../../../components/component-docs/demo.script.tsx',
			'../../../components/component-docs/canvas.script.tsx',
			'../../../components/component-docs/controls.script.tsx',
		],
	},
};


# Treegrid

<p class="docs-lede">Treegrids combine tree hierarchy with tabular columns, such as file explorers with size and date columns, or nested budgets.</p>

## Try it

<Demo of={Default} meta={TreegridMeta} />

## Usage

Define `columns` and nested `rows` with `id`, `cells`, and optional `children`. Bind `value` to the selected row id.

```tsx
import { RuiTreegrid, type RuiTreegridRow } from '@ecopages/radiant-ui/treegrid';

const rows: RuiTreegridRow[] = [
  {
    id: 'docs',
    cells: ['docs'],
    children: [{ id: 'intro', cells: ['introduction.md'] }],
  },
];

<RuiTreegrid columns={['Name']} rows={rows} value="intro" label="Repository" />
```

## Custom markup

`<rui-treegrid>` coordinates any light-DOM tree that matches its query contract. The `RuiTreegrid` helper stamps these targets; it is not required.

```tsx
import '@ecopages/radiant-ui/treegrid';

<rui-treegrid value="intro" label="Repository">
  <div role="treegrid" aria-label="Repository">
    <div role="row">
      <div role="columnheader">Name</div>
    </div>
    <div role="row" data-row-id="docs" aria-expanded="true">
      <div role="gridcell">docs</div>
    </div>
    <div role="rowgroup">
      <div role="row" data-row-id="intro">
        <div role="gridcell">introduction.md</div>
      </div>
    </div>
  </div>
</rui-treegrid>;
```

## Tree vs treegrid

<p>Use Treegrid when rows have multiple columns of data. Use Tree for single-column hierarchies.</p>

## Theming

Treegrid maps to **semantic surface and status roles**, never Tailwind palette steps:

The shared layout uses one outer frame, compact non-wrapping cells, and no vertical rules between cells. The complete themes add their file-browser character on top of that contract: VS Code is flat and compact, GitHub uses a bordered list with horizontal separators, macOS uses a rounded source-list surface, Mac OS Classic uses a sunken field with a Platinum header and triangle disclosure, and Windows 95 uses a recessed field with raised headers and square plus/minus affordances.

| Part | CSS roles |
| --- | --- |
| Surface (`rui-treegrid`) | `border`, `background`, `on-background`, `radius-container`, `shadow-sm` |
| Header cells | `surface`, `on-background` |
| Row hover | `surface` |
| Selected row | `primary-container`, `on-primary-container` |
| Cell focus | `focus-ring`, `z-dropdown` |
| Geometry | `--space-control-x` (cell padding) |
| Nested rows | `--rui-treegrid-indent` (per-level inline indentation) |

Override at the theme layer, not in component CSS.

## API

`RuiTreegrid` is a custom element (`<rui-treegrid>`) that owns expand/collapse and cell-only roving-tabindex navigation; the `RuiTreegrid` JSX view authors rows, groups, and header cells.

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `label` | `string` | `''` | Accessible name for the treegrid. |
| `value` | `string` | `''` | Selected row's `data-row-id`. |

### Light-DOM contract

| Target | Required | Host writes | Author owns |
| --- | --- | --- | --- |
| `[role="treegrid"]` | yes | — | root surface; view sets `aria-label` |
| `[role="row"][data-row-id]` | yes | `aria-expanded`, `data-depth`, `--rui-treegrid-depth` | `data-row-id`, initial `aria-expanded` |
| `[role="gridcell"]` | yes | `aria-selected`, roving `tabIndex` | cell content |
| `[role="rowgroup"]` | for branches | `hidden` | sibling after expandable row |

Header rows and `[role="columnheader"]` cells are presentation-only. Nested hosts: none.

### Events

| Event | Detail | Description |
| --- | --- | --- |
| `rui-change` | `{ rowId: string, columnIndex: number }` | Emitted when a cell activates. |

### Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `columns` | `JsxRenderable[]` | | Header cell content for the `RuiTreegrid` view. |
| `rows` | `RuiTreegridRow[]` | | Nested row structure. |

`RuiTreegridRow` is `{ id, cells, children?, expanded? }`.

### View helpers

| Component | Target stamped | Notes |
| --- | --- | --- |
| `RuiTreegrid` | `[role="treegrid"]` with `data-ref="root"` | Accepts `columns` + `rows` or `children`. |

### CSS classes

Public BEM classes (documented via `@cssclass`):

| Class | Description |
| --- | --- |
| `.rui-treegrid` | Root surface inside the host (`role="treegrid"`). |
| `.rui-treegrid__row` | Data row (`role="row"`). |
| `.rui-treegrid__row--header` | Header row. |
| `.rui-treegrid__group` | Collapsible row group (`role="rowgroup"`). |
| `.rui-treegrid__cell` | Data cell (`role="gridcell"`). |
| `.rui-treegrid__cell--header` | Column header cell (`role="columnheader"`). |

### Theme roles

| Part | CSS variables consumed |
| --- | --- |
| Surface | `--border`, `--background`, `--on-background`, `--radius-container`, `--shadow-control` |
| Header cells | `--surface`, `--on-background` |
| Row hover | `--surface` |
| Selected row | `--primary-container`, `--on-primary-container` |
| Cell focus | `--focus-ring`, `--z-dropdown` |
| Cell padding | `--space-control-x` |

## Accessibility

- Treegrid exposes `role="treegrid"` with grid navigation semantics.
- Column headers provide context for cell values.
- Expandable rows communicate `aria-expanded` on parent rows.
