Founding price: $35, then $79.99 after 23:59 UTC on 4 November, then $79.99
Get ProAn ARIA tree with roving focus, arrow-key navigation, typeahead, single or multiple selection and a headless hook, in one file.
Derived from this item’s props table, because it declares no controls of its own. Changes drive the preview live.
Use it for any hierarchy a person browses and selects from: files, categories, outlines, org units. useTree is the same behaviour without markup, for trees that need their own rows.
Do not use it for a flat list (use a listbox) or for a menu of commands (use a dropdown menu). For navigation links use tree-nav; for checkboxes use checkbox-tree.
import { TreeView, type TreeNode } from "@/components/ui/tree-view"const nodes: TreeNode[] = [
{
id: "app",
label: "app",
children: [
{ id: "page", label: "page.tsx" },
{ id: "layout", label: "layout.tsx" },
],
},
{ id: "readme", label: "README.md" },
]
<TreeView
aria-label="Project files"
nodes={nodes}
defaultExpanded={["app"]}
selectionMode="multiple"
onSelectedChange={(ids) => console.log(ids)}
/>| Prop | Type | Default | Description |
|---|---|---|---|
nodes | TreeNode[] | - | The tree. A node has id, label, children, and optionally hasChildren, disabled, icon, href, data. |
expanded / defaultExpanded / onExpandedChange | string[] | [] | Open branches, controlled or not. |
selected / defaultSelected / onSelectedChange | string[] | [] | Selected ids. |
selectionMode | "none" | "single" | "multiple" | "single" | How rows are selected. |
selectionBehavior | "replace" | "toggle" | "replace" | Multiple mode: a click replaces the selection (Ctrl/Cmd and Shift extend it), or toggles the row, which also works by touch. |
onActivate | (node) => boolean | void | - | Enter or double click. Return false to keep the default (open or close a branch). |
isSelectable | (node) => boolean | - | Rows that return false open and close instead of selecting. A disabled branch also opens and closes on click. |
indent / guides | number / boolean | 16 / false | Pixels per level and vertical guide lines. |
renderLabel / renderTrailing | (row) => ReactNode | - | Replace the label text or add content at the end of a row. |
rowProps / isRowLoading | (row) => ... | - | Extra attributes per row, and a busy state. |
dir | "ltr" | "rtl" | detected | Reading direction for Left and Right. |
Motion respects prefers-reduced-motion: the only animation is the loading spinner, which is switched off. Layout uses logical properties, so the component works in right-to-left languages.
The file also exports the pure functions the other tree components share, so you can use them with any state:
flattenTree, findNode, findPath, moveNode, shiftNode, indentNode, outdentNode, filterTree, pruneTree,
computeCheckStates, toggleChecked and findTypeaheadMatch. They never mutate: each returns a new tree, or the same
array when the change is not allowed.
useTree({ nodes, ... }) returns rows, rootProps, getRowProps(row) and the actions. Use it when a row needs its
own markup (a table, a virtual list, a card): spread rootProps on the container and getRowProps(row) on each row.
The component ships with a Testing Library test (tree-view.test.tsx) that covers its keyboard paths, roles and states.