Display and navigate hierarchies — a CMS page tree, category tree, media folders, a department tree, file explorer panes. A flat WAI-ARIA tree (level, set size and position on every row) with single, multiple or tri-state checkbox selection, lazy children with retry, filtering that keeps ancestors, hover actions, indent guides and virtualisation for thousands of nodes. The useTree hook underneath is exported for custom trees.
Double-click or press Enter to open a page.
Installation
The whole kit as one package — get updates by bumping the version. Add the styles to globals.css: @import "@vamos-ui/react/styles.css";
Usage
import { TreeView, type TreeNode } from "@vamos-ui/react";
<TreeView aria-label="Pages" data={pages} defaultExpanded={["company"]} onAction={(node) => openPage(node.id)} />Keyboard: ↑ ↓ move, → opens or enters a branch, ← closes or goes to the parent (swapped in RTL), Home / End, * opens all siblings, type-ahead (folded: “ist” finds “İstanbul”), Space toggles the check or selection, Enter runs onAction.
Drag-to-move comes from Sortable tree (Editors & drag-and-drop), which reuses useTree.
Examples
Tri-state checkboxes
selectionMode="checkbox" cascades to descendants and shows mixed parents. checkValueMode="leaf" keeps only leaf ids (permission keys). Disabled nodes keep their state.
3 permissions granted · “Change plan” is owner-only
Lazy folders
hasChildren + loadChildren: a spinner replaces the chevron while loading (aria-busy). A failed load shows Retry in the row.
Filter keeps ancestors
filter keeps matches and their ancestors, opens the path and highlights the match. The match count is announced.
5,550 nodes (virtual)
virtual renders only the rows in view; keyboard focus scrolls rows into view first. Set a height.
API
TreeView
| Prop | Type | Default |
|---|---|---|
data{ id, label, children?, hasChildren?, disabled?, icon?, badge?, description?, keywords? }. | TreeNode[] | — |
aria-labelAccessible name of the tree (required unless labelled by a heading). | string | — |
selectionModecheckbox = tri-state checks. | "none" | "single" | "multiple" | "checkbox" | "single" |
selected / defaultSelectedSelected ids (checked ids in checkbox mode). | string[] | — |
onSelectedChangeCalled with the new selection. | (ids) => void | — |
expanded / defaultExpandedOpen branches (controlled / initial). | string[] | — |
onExpandedChangeCalled when a branch opens or closes. | (ids) => void | — |
cascadeCheckbox mode: parents check descendants. | boolean | true |
checkValueModeCheckbox mode: shape of the value. | "all" | "parent" | "leaf" | "all" |
isSelectableWhich rows can be selected. | (node, hasChildren) => boolean | — |
loadChildrenLazy children. | (node, signal) => Promise<TreeNode[]> | — |
filterLocale-folded filter; keeps ancestors. | string | — |
onActionEnter or double click. | (node) => void | — |
renderActionsTrailing buttons, revealed on hover and focus. | (node) => ReactNode | — |
renderLabelReplace a row's label. | (item) => ReactNode | — |
virtualRender only visible rows. | boolean | false |
heightScroll height. | number | string | 360 when virtual |
indentIndent per level (px, logical start). | number | 16 |
guidesIndent guide lines. | boolean | true |
variantsidebar: accent current row. | "default" | "sidebar" | "default" |
sizeRow height (follows density). | "sm" | "md" | "md" |
emptyStateShown when data is empty. | ReactNode | — |
labelsBuilt-in strings (namespace treeView). | Partial<TreeViewLabels> | — |
useTree(options)
| Prop | Type | Default |
|---|---|---|
returns itemsFlattened visible rows with level, posinset, setsize, expanded, checked, loading, error, guides. | TreeItemState[] | — |
getTreeProps / getItemPropsSpread on the tree element and each row: roles, roving tabindex, ARIA state, full keyboard. | () => props | — |
toggleExpanded / toggleChecked / select / focusItem / reloadImperative helpers. | (id) => void | — |
getPath / getNode / getChildrenLookups (paths for breadcrumbs and triggers). | … | — |