Vanilla JS Tree Data Tables: Hierarchical Rows in TypeScript (2026)

Vanilla TSTutorialTree Data

Render hierarchical / tree data in a vanilla TypeScript table with expand and collapse—strict TypeScript examples for simple-table-core and a comparison to Tabulator and Handsontable.

For Vanilla JS / TypeScript developers building data grids in 2026.

Org charts, file systems, BOMs, ledger structures—real-world data is rarely flat. Tree-data tables let users expand and collapse rows in place to drill down without losing context.

This tutorial covers tree-data patterns in vanilla TypeScript: parent/child rendering, lazy loading children, and expand-all / collapse-all controls. We compare Tabulator, Grid.js, Handsontable, and simple-table-core.

simple-table-core uses the same component for flat and hierarchical data—no separate TreeTable to learn, no plugin to install.

Why it matters

Drill-down without losing context

Users see parent and children in the same surface; no modal or detail-page round-trips.

Compact wide hierarchies

Collapse low-level children; expand specific branches users care about.

Reusable for many domains

Folders, regions, org charts, and chart-of-accounts all share the pattern.

Combinable with sort/filter

Filter the leaves; the right ancestors stay visible to preserve context.

Vanilla JS / TypeScript library comparison

LibrarySupportNotes
simple-table-coreBuilt-in (MIT)Hierarchical rows with built-in expand/collapse and chevron renderer.
TabulatorBuilt-indataTree: true; supports lazy loading via dataTreeChildField.
Grid.jsManualNo native tree—pre-flatten and render hierarchy yourself.
HandsontableBuilt-in (commercial)Tree built-in but commercial license required.
jSpreadsheetManualSpreadsheet-style; not designed for hierarchical data.

Implementation: simple-table-core

Provide hierarchical rows with depth per the docs. simple-table-core renders chevrons and handles expand/collapse state for you.

For very deep trees (10+ levels), consider adding a "collapse all" control above the grid. Combine with virtualization to keep performance flat for thousands of rows.

Common pitfalls

Performance on deep trees

Problem: Expanding a node with 10k descendants stalls the UI.

Solution: Lazy-load children on expand, and rely on built-in virtualization.

Filtering hides ancestors

Problem: Searching matches a leaf, but the tree breaks because parents are filtered out.

Solution: Filter while preserving ancestor rows. simple-table-core supports this with includeAncestors filter mode.

Memory leaks on SPA navigation

Problem: Listeners persist after the host element is removed.

Solution: Call table.dispose() on cleanup so listeners are removed.

Indent ambiguity

Problem: Multiple columns indent independently; users can't tell where children belong.

Solution: Indent only the tree column (usually the first). simple-table-core does this automatically with expandable: true.

Frequently asked questions

Does simple-table-core have a separate TreeTable?
No—the same vanilla SimpleTable handles flat and hierarchical data. Provide depth and expansion information per the nested tables docs to render a tree.
Does it work in a web component?
Yes. Mount inside the shadow root: pass shadowRoot.querySelector('#host') as the element.
Can I use tree data with column pinning?
Yes. Tree data, pinning, virtualization, sorting, and filtering all combine.

Wrap-up

Tree data in vanilla JS / TS is straightforward with simple-table-core—provide hierarchy on each row and the grid handles the rest. Tabulator supports it natively too; Grid.js requires DIY; Handsontable is commercial.

If you need hierarchical data alongside virtualization and pinning, simple-table-core is the focused MIT pick.

Add tree data to your vanilla TS grid

simple-table-core ships tree data, virtualization, pinning, and editing in one MIT package—~70 kB gzipped, strict TypeScript, ESM-first.