Advanced Tree Data Grid
Tree Data is the grid pattern for hierarchical records that still need spreadsheet-grade behavior. A tree table should not force users to choose between hierarchy and data-grid features: they still need virtual scrolling, column resizing, sorting, filtering, row selection, editing, drag-and-drop, and controlled state.
Core TypeScript tree example
Tree-like rows do not require Pro when your application only needs a visual hierarchy. The existing TypeScript core tree demo flattens parent-child records into display order, adds a level field, and renders indentation in a normal cell template.
See TypeScript Tree Data for the maintained core example source and framework notes. Use this core pattern for read-only outlines, category lists, document sections, or cases where your application owns expand/collapse outside the grid. Use the Pro plugin when the tree itself needs grid-native controls and behavior.
Why flat tree data works better
Deeply nested arrays are convenient for simple menus, but they become expensive in an editable data grid. Real applications need stable row IDs, row-level permissions, server updates, filtering, sorting, drag-and-drop, and partial refreshes. A flat tree model keeps each row addressable:
type WorkItem = {
id: string;
parentId: string | null;
name: string;
owner: string;
status: 'Open' | 'Blocked' | 'Done';
};
const source: WorkItem[] = [
{ id: 'launch', parentId: null, name: 'Launch plan', owner: 'Maya', status: 'Open' },
{ id: 'design', parentId: 'launch', name: 'Design review', owner: 'Ivo', status: 'Done' },
{ id: 'docs', parentId: 'launch', name: 'Docs update', owner: 'Nia', status: 'Blocked' },
];The hierarchy is defined by id and parentId, while the grid still receives a normal row array.
Core setup
The Pro setup is the same across frameworks: register the plugins, mark the hierarchy column, pass a tree config, and keep the row source flat. React, Vue, Angular, Svelte, Stencil, and TypeScript examples differ only in how these objects are bound to the grid.
import {
TreeDataPlugin,
RowOrderPlugin,
RowSelectPlugin,
StickyCellsPlugin,
TREE_EXPAND_ALL_EVENT,
TREE_COLLAPSE_ALL_EVENT,
} from '@revolist/revogrid-pro';
const columns = [
{
prop: 'name',
name: 'Task',
size: 320,
tree: true,
rowSelect: true,
rowDrag: true,
sortable: true,
},
{ prop: 'owner', name: 'Owner', size: 140 },
{ prop: 'status', name: 'Status', size: 140 },
];
const plugins = [
TreeDataPlugin,
RowOrderPlugin,
RowSelectPlugin,
StickyCellsPlugin,
];
const tree = {
idField: 'id',
parentIdField: 'parentId',
rootParentId: null,
expandedRowIds: new Set(['launch']),
stickyParents: true,
};Framework wrappers pass these values as props or bindings. Plain TypeScript assigns them to the revo-grid element:
grid.columns = columns;
grid.source = source;
grid.plugins = plugins;
grid.tree = tree;Expansion state
Use expandedRowIds when expansion should be restored from a route, local storage, or backend preference. Use expandAll: true for documentation, demos, or small trees where every branch should start open.
Toolbar actions can dispatch the public tree events:
grid.dispatchEvent(new CustomEvent(TREE_EXPAND_ALL_EVENT));
grid.dispatchEvent(new CustomEvent(TREE_COLLAPSE_ALL_EVENT));What the Pro plugin adds
RevoGrid Pro Tree Data is more than indentation. It is designed for advanced data grid tree structures:
| Capability | Why it matters |
|---|---|
| Flat source rows | Stable IDs, simple backend contracts, fast updates |
tree: true column | Consistent expand controls and indentation in the data grid |
| Computed tree metadata | The original row objects stay clean |
| Expand and collapse events | External toolbars and saved state are straightforward |
| Row order integration | Dragging rows can update hierarchy relationships |
| Row selection integration | Parent and descendant selection can be coordinated |
| Sticky parent rows | Users keep branch context while scrolling long trees |
| Virtual rendering | Large hierarchies remain practical |
Tree Data vs row grouping
Tree Data and row grouping can both create expandable-looking sections, but they solve different data problems.
Use Tree Data when parent-child relationships are part of the records themselves. A row has a stable id, a parentId, and a real position in a hierarchy: departments contain teams, folders contain files, projects contain tasks, bill-of-material rows contain child parts. Moving a row can change its parent. Expanding a parent shows its actual descendants.
Use row grouping when the source rows are still flat records and the hierarchy is only a view created from shared values. For example, orders can be grouped by country, then city, then status. The group headers are generated summaries, not source rows with IDs. Changing the grouping props changes the view, but it does not change each order's parent.
| Use case | Tree Data | Row Grouping |
|---|---|---|
| Data model | Explicit parent-child records | Flat rows grouped by field values |
| Parent rows | Real rows from your source | Generated group headers |
| Identity | Stable id and parentId | Group keys from values such as status |
| Best for | Org charts, folders, tasks, BOMs | Reports, category breakdowns, grouped lists |
| Reordering | Can mean reparenting a row | Usually changes row order inside a grouped view |
| Configuration | Tree column plus tree config | grouping = { props: [...] } |
If a row belongs under one specific parent even when sorting or filtering changes, use Tree Data. If rows are being arranged temporarily by common values for analysis, use Row Grouping.
Design recommendations
Use Tree Data when hierarchy is part of the record identity. Use row grouping when hierarchy is only a view over flat categories. Use master-detail rows when each parent opens a separate detail surface rather than a real branch of rows.
For production trees, keep the first tree column wide enough for indentation and labels, place status or numeric columns to the right, and avoid expensive templates in every tree cell. If the hierarchy can be very deep, set a clear maximum supported depth in your product UI and validate imported data before it reaches the grid.