Tree Data
TreeKit works with one shape: a plain array of TreeNode objects.
interface TreeNode<T = unknown> {
id: string; // unique across the ENTIRE tree
label: string; // rendered by the default node renderer
children?: TreeNode<T>[];
disabled?: boolean;
hasChildren?: boolean; // for lazily-loaded nodes — see Async Loading
icon?: ReactNode; // per-node icon override
data?: T; // your own domain data, fully typed
}Unique ids
Every id must be unique across the whole tree, not just among siblings — TreeKit indexes the tree by id internally for O(1) parent/ancestor lookups, and duplicate ids make selection and expansion state ambiguous. In development, TreeKit logs a console error if it finds duplicates; nothing crashes, but behavior around the duplicated node is undefined.
Attaching your own data
TreeNode is generic. Use the data field for anything domain-specific — a file path, a permission key, a database row — and it comes through fully typed wherever TreeKit hands you a node back (callbacks, renderNode, the utility functions):
interface FileMeta {
path: string;
sizeBytes: number;
}
const data: TreeNode<FileMeta>[] = [
{ id: "readme", label: "README.md", data: { path: "/README.md", sizeBytes: 1024 } },
];
<TreeKit
data={data}
onNodeDoubleClick={(node) => {
// node.data is typed as FileMeta here
openFile(node.data.path);
}}
/>TreeKit never mutates your data
data is treated as immutable. Expansion, checked, and selection state all live in separate id sets (either state you own, for controlled usage, or state TreeKit owns internally, for uncontrolled usage) — never written back onto your node objects. This means the same data array can safely be shared, memoized, or come straight from a server response.
Async-loaded children
onLoadChildren: TreeKit merges the returned children into an internal copy of the tree without touching your original objects. See Async Loading.Leaf vs. branch nodes
A node is a "leaf" (no expand chevron, checkbox only) when it has no children array and no hasChildren flag. An empty array (children: []) is treated the same as a leaf — only a non-empty array or an explicit hasChildren: true renders a chevron.