Skip to content

Svelte Tree Data Grid

Svelte can use the free/core tree pattern with the same RevoGrid Web Component API: flatten your hierarchy, keep a level field, and render indentation in a cell template.

INFO

The shared live core tree demo is currently published for TypeScript, React, Vue 3, and Angular. Svelte uses the same flat source and cell-template approach.

Tree-like rows do not require Pro when your application only needs a visual hierarchy. The free/core approach is to flatten parent-child records into display order, add a level field, and render indentation in a cell template.

This keeps RevoGrid simple: the grid receives a normal source array, virtualization still works, and your application owns the hierarchy transformation.

Tree model

Start with records that have an ID and a parent ID:

ts
const inputData = [
  { id: 4, parentId: 0, name: 'Antoni father for 2' },
  { id: 5, parentId: 4, name: 'Odin' },
  { id: 6, parentId: 4, name: 'John' },
  { id: 1, parentId: 0, name: 'Mary mother for 2' },
  { id: 2, parentId: 1, name: 'Howard has also 2' },
];

Convert that data into a flat display list before assigning it to the grid:

ts
function buildTreeData(rows: typeof inputData, rootParentId = 0) {
  const childrenByParent = new Map<number, typeof inputData>();

  for (const row of rows) {
    const children = childrenByParent.get(row.parentId) ?? [];
    children.push(row);
    childrenByParent.set(row.parentId, children);
  }

  const result: Array<(typeof inputData)[number] & { level: number }> = [];

  function appendChildren(parentId: number, level = 0) {
    for (const child of childrenByParent.get(parentId) ?? []) {
      result.push({ ...child, level });
      appendChildren(child.id, level + 1);
    }
  }

  appendChildren(rootParentId);
  return result;
}

Indented cell template

The core demo uses a normal cell template to show hierarchy depth:

ts
const columns = [
  {
    prop: 'name',
    name: 'Tree',
    size: 300,
    cellTemplate(h, { value, model }) {
      return h(
        'div',
        {
          style: {
            marginLeft: `${(model?.level ?? 0) * 30}px`,
          },
        },
        value || '',
      );
    },
  },
  {
    prop: 'level',
    readonly: true,
  },
];

Use the core approach when the hierarchy is read-only or mostly decorative: category outlines, simple nested lists, document sections, or a tree table where your app handles all expand/collapse logic outside the grid.

Core limits

The free/core pattern is intentionally lightweight. It does not add built-in expand/collapse controls, automatic parent-child visibility, tree-aware drag-and-drop, sticky parent rows, or parent/descendant row selection behavior. Those behaviors belong in the Pro Tree Data plugin.

Svelte notes

  • Keep the input hierarchy and flattened treeData in component state or a store.
  • Reassign treeData when the hierarchy changes.
  • In SvelteKit, render RevoGrid only on the client, as described in the main Svelte guide.
svelte
<RevoGrid
  columns={columns}
  source={treeData}
  readonly
/>

Pro Tree Data

RevoGrid Pro adds a dedicated TreeDataPlugin for advanced data grid tree structures. It keeps the original source flat, computes hierarchy metadata internally, and renders tree controls in the column marked with tree: true.

RevoGrid Pro

Use the Pro plugin when hierarchy is not just indentation: users need expand/collapse controls, persistent expansion state, filtering and sorting with hierarchy awareness, row selection across descendants, drag-and-drop reparenting, or sticky parent rows while scrolling long branches.

Pro data model

The Pro model also starts from stable IDs and parent IDs:

ts
type TeamRow = {
  id: string;
  parentId: string | null;
  name: string;
  role: string;
  budget: number;
};

const source: TeamRow[] = [
  { id: 'eng', parentId: null, name: 'Engineering', role: 'Department', budget: 850000 },
  { id: 'platform', parentId: 'eng', name: 'Platform', role: 'Team', budget: 320000 },
  { id: 'api', parentId: 'platform', name: 'API', role: 'Squad', budget: 140000 },
  { id: 'design', parentId: null, name: 'Design', role: 'Department', budget: 260000 },
];

Unlike the core example, you do not add level yourself. The plugin derives level, visibility, expanded state, and child metadata from the tree config.

Pro column and plugins

Mark one hierarchy column with tree: true, register TreeDataPlugin, and pass the tree config:

ts
import {
  TreeDataPlugin,
  RowOrderPlugin,
  RowSelectPlugin,
  StickyCellsPlugin,
  TREE_EXPAND_ALL_EVENT,
  TREE_COLLAPSE_ALL_EVENT,
} from '@revolist/revogrid-pro';

const columns = [
  {
    prop: 'name',
    name: 'Team',
    size: 300,
    tree: true,
    sortable: true,
    rowSelect: true,
    rowDrag: true,
  },
  { prop: 'role', name: 'Role', size: 160 },
  { prop: 'budget', name: 'Budget', size: 140 },
];

const plugins = [
  TreeDataPlugin,
  RowOrderPlugin,
  RowSelectPlugin,
  StickyCellsPlugin,
];

const tree = {
  idField: 'id',
  parentIdField: 'parentId',
  rootParentId: null,
  expandedRowIds: new Set(['eng']),
  stickyParents: true,
};

Framework wrappers pass these values as props or bindings. Plain TypeScript assigns them to the revo-grid element.

Expand and collapse controls

The Pro plugin exposes events for toolbar buttons and external controls:

ts
grid.dispatchEvent(new CustomEvent(TREE_EXPAND_ALL_EVENT));
grid.dispatchEvent(new CustomEvent(TREE_COLLAPSE_ALL_EVENT));

Use expandAll: true when every branch should open on first render. Use expandedRowIds when you want controlled initial state or persisted expansion.

Production guidance

  • Keep IDs stable across refreshes so expanded state survives data updates.
  • Use parentId: null or another explicit rootParentId consistently.
  • Keep only one tree column active; use normal columns for the rest of the row data.
  • Combine tree data with virtualization for large hierarchies instead of rendering nested DOM lists.
  • Add StickyCellsPlugin only when sticky parent rows are useful for long branches.
  • Reconcile drag-and-drop changes in your application store before saving to the backend.
  • Prefer flat server payloads for large trees; they are simpler to page, diff, validate, and persist.