bo-grid v3.1.0

Grouping & pivot

.md

Tree data

Show hierarchical rows as an expandable tree, from children you already have or loaded from a server on expand.

Tree data shows rows that contain rows: folders and files, an org chart, accounts and sub-accounts. Tell the grid how to find a row’s children, and rows become the roots of an indented tree with a chevron on every row that has children.

Expand the folders with a click, or with → and ← on the focused row.

Tree.svelte Open GitHub ↗

Children you already have

getChildren returns a row’s children, or undefined (or an empty array) for a leaf:

<script lang="ts">
  import { Grid, type GridRow } from 'bo-grid';

  let rows = $state<GridRow[]>([
    { id: 1, name: 'src', children: [{ id: 2, name: 'app.ts' }] },
    { id: 3, name: 'README.md' },
  ]);
</script>

<Grid {rows} {columns} height={520} getChildren={(r) => r.children as GridRow[] | undefined} />

The first column is indented by depth and carries the chevron. Every row in the tree needs an id unique across the whole tree, not just among its siblings.

In tree mode the grid shows the tree as given: filtering, sorting, grouping and pagination do not apply to it.

Children loaded on expand

For a hierarchy too large to send up front, use loadChildren instead of getChildren. It returns a promise of a row’s children and runs the first time the row is expanded; the grid shows a loading row meanwhile and keeps the children once loaded. Pair it with hasChildren, a cheap test that decides which rows get a chevron before anything loads:

<Grid
  {rows}
  {columns}
  height={520}
  hasChildren={(r) => r.kind === 'folder'}
  loadChildren={(r) => fetch(`/api/children/${r.id}`).then((res) => res.json())}
/>

A server-backed tree, with a loading row on each first expand:

LazyTree.svelte Open GitHub ↗

Keyboard and screen readers

With tree data the grid takes the treegrid role: rows expose aria-level and aria-expanded, → expands a collapsed row and ← collapses an expanded one. Other keys work as in a flat grid; see Keyboard & hotkeys.

Reference

Grid props from the types
getChildren (row: GridRow) => GridRow[] | undefined

Tree data: return a row's children (undefined/empty = leaf). When set, rows are the roots; the grid renders an indented, expandable tree. In-memory mode; filter/sort/group/paginate are not applied to the tree.

Example
<Grid rows={roots} {columns} height={480} getChildren={(row) => row.children as GridRow[] | undefined} />
loadChildren (row: GridRow) => Promise<GridRow[]>

Async tree data: load a row's children on expand (server-backed trees). Returns a promise; the grid shows a loading row, then caches the result. Pair with hasChildren (a cheap predicate) so chevrons show without loading. Use instead of getChildren. In-memory roots.

Example
<Grid rows={roots} {columns} height={480}
  loadChildren={(row) => fetch(`/api/accounts/${row.id}/children`).then((r) => r.json())}
  hasChildren={(row) => Number(row.childCount) > 0} />
hasChildren (row: GridRow) => boolean

Cheap predicate for whether a row has children (drives the expand chevron without loading). Required with loadChildren; optional with getChildren.

Example
<Grid rows={roots} {columns} height={480} {loadChildren} hasChildren={(row) => row.kind === 'folder'} />