VayuUI

Tree

A compound tree component with single-select and checkbox modes, search, keyboard navigation, cascade checking, and ARIA tree semantics.

Installation

npx vayu-ui-cli@latest add tree

Usage

Tree
src
components
package.json
tsconfig.json
README.md
const data: TreeNode[] = [
  {
    id: "src",
    label: "src",
    children: [
      { id: "app.tsx", label: "app.tsx" },
      { id: "index.tsx", label: "index.tsx" },
    ],
  },
  { id: "package.json", label: "package.json" },
]

<Tree data={data} aria-label="Project files">
  <Tree.Search searchQuery={query} onSearchChange={setQuery} />
  <Tree.Actions onExpandAll={expandAll} onCollapseAll={collapseAll} />
  <Tree.Container>
    <Tree.Nodes nodes={data} />
  </Tree.Container>
</Tree>

<Tree data={data} mode="checkbox" onCheck={(keys) => setChecked(keys)}>
  <Tree.Container>
    <Tree.Nodes nodes={data} />
  </Tree.Container>
</Tree>

Anatomy

import { Tree } from 'vayu-ui';
import type { TreeNode } from 'vayu-ui';

const data: TreeNode[] = [
  { id: 'root', label: 'Root', children: [{ id: 'child', label: 'Child' }] },
];

<Tree data={data} aria-label="My tree">
  <Tree.Search searchQuery={query} onSearchChange={setQuery} />
  <Tree.Actions onExpandAll={handleExpandAll} onCollapseAll={handleCollapseAll} />
  <Tree.Container>
    <Tree.Nodes nodes={data} />
  </Tree.Container>
</Tree>;
  • Tree — Root provider that manages expand, select, and check state. Wraps all sub-components.
  • Tree.Search — Controlled search input for filtering nodes. Requires searchQuery and onSearchChange.
  • Tree.Actions — Toolbar with Expand All and Collapse All buttons.
  • Tree.Container — Bordered container with a built-in empty state fallback.
  • Tree.Nodes — Renders an array of TreeNode objects. Typically receives the filtered data array.
  • Tree.Node — Renders a single node with optional children. Used internally by Tree.Nodes or manually for custom layouts.

Accessibility

  • Keyboard Support:
    • ArrowDown — Move focus to the next visible node.
    • ArrowUp — Move focus to the previous visible node.
    • ArrowRight — Expand a collapsed node, or move focus to the first child of an expanded node.
    • ArrowLeft — Collapse an expanded node, or move focus to the parent of a collapsed node.
    • Home — Move focus to the first visible node.
    • End — Move focus to the last visible node.
    • Enter — Toggle expand/collapse on parent nodes, select in normal mode, or toggle checkbox in checkbox mode.
    • Space — Select a node in normal mode, or toggle checkbox in checkbox mode.
  • ARIA Attributes:
    • Root has role="tree" with aria-label or aria-labelledby.
    • aria-multiselectable is set to true when mode="checkbox".
    • Each node has role="treeitem" with aria-expanded, aria-selected, aria-disabled, and aria-level.
    • Child groups use role="group" with aria-label describing the parent.
    • Checkboxes use role="checkbox" with aria-checked (true, false, or "mixed" for indeterminate).
    • Search input uses role="searchbox" with aria-label="Search tree".
    • Action buttons use role="toolbar" with aria-label="Tree actions".
  • Focus Behavior:
    • Only the focused node has tabIndex={0}; all others have tabIndex={-1}.
    • Focus ring is shown via ring-2 ring-inset ring-focus.
    • Arrow key navigation cycles through visible nodes only, skipping collapsed children.

Screen reader behavior

When a user navigates to a tree node, the screen reader announces the node label, its nesting level (via aria-level), and its expanded or collapsed state (via aria-expanded). In checkbox mode, the checked state is announced as checked, unchecked, or indeterminate (via aria-checked). When a node is expanded or collapsed, the screen reader announces the state change. Parent nodes are announced with their child count via the role="group" container. Disabled nodes are announced as disabled (via aria-disabled). Users can navigate between nodes using arrow keys, and the screen reader announces each node as it receives focus. The search input is announced as a search box, and toolbar buttons are announced with their respective labels.

Component Folder Structure

Tree/
├── Tree.tsx            # Root component with context provider and state management
├── TreeNode.tsx        # Individual node rendering with expand, select, and checkbox
├── TreeContainer.tsx   # Bordered container with empty state fallback
├── TreeActions.tsx     # Expand All / Collapse All toolbar
├── TreeSearch.tsx      # Controlled search input with clear button
├── hooks.ts            # useTree hook for accessing tree context
├── utils.ts            # Tree traversal utilities (flatten, find, filter)
├── types.ts            # TypeScript type definitions
└── index.ts            # Re-exports all components, hook, and types

Props

Tree (Root)

PropTypeDefaultDescription
dataTreeNode[]Tree data structure. Required.
mode'normal' | 'checkbox''normal'Selection mode.
variant'default' | 'filled' | 'bordered' | 'minimal''default'Visual style variant.
size'sm' | 'md' | 'lg''md'Component size.
showLinesbooleantrueShow connecting lines between nodes.
showIconsbooleantrueShow file/folder icons on nodes.
defaultExpandAllbooleanfalseExpand all nodes on mount.
defaultExpandedKeys(string | number)[][]Initially expanded node keys.
expandedKeys(string | number)[]Controlled expanded state.
onExpandedChange(keys: (string | number)[]) => voidFired when expand state changes.
selectedKeystring | number | nullControlled selected node (normal mode).
defaultSelectedKeystring | numberInitially selected node.
onSelect(key, node) => voidFired when a node is selected.
checkedKeys(string | number)[]Controlled checked nodes (checkbox mode).
defaultCheckedKeys(string | number)[][]Initially checked node keys.
onCheck(keys, nodes) => voidFired when checkboxes change.
checkStrictlybooleanfalseDisable parent-child checkbox cascade.
disabledbooleanfalseDisable the entire tree.
onNodeClick(node: TreeNode) => voidFired on any node click.
renderActions(node: TreeNode) => ReactNodeRender action buttons per node.
aria-labelstring'Tree'Accessible label for the tree.
aria-labelledbystringID of the element labeling the tree.
classNamestringAdditional CSS classes.

Tree.Search

PropTypeDefaultDescription
searchQuerystringCurrent search query. Required.
onSearchChange(query: string) => voidCallback when query changes. Required.
placeholderstring'Search tree…'Input placeholder text.
classNamestringAdditional CSS classes.

Tree.Actions

PropTypeDefaultDescription
onExpandAll() => voidExpand all nodes callback. Required.
onCollapseAll() => voidCollapse all nodes callback. Required.
classNamestringAdditional CSS classes.

Tree.Container

PropTypeDefaultDescription
childrenReactNodeTree content. Required.
emptyReactNodeCustom empty state fallback.
classNamestringAdditional CSS classes.

Tree.Nodes

PropTypeDefaultDescription
nodesTreeNode[]Array of tree nodes to render. Required.

Tree.Node

PropTypeDefaultDescription
nodeTreeNodeSingle tree node object. Required.
levelnumber0Nesting depth (0-based).

TreeNode (Data Type)

PropTypeDefaultDescription
idstring | numberUnique identifier. Required.
labelstringDisplay text. Required.
iconReactNodeCustom icon override.
childrenTreeNode[]Nested child nodes.
disabledbooleanfalseDisable this node.
selectablebooleantrueAllow selection on this node.
metadataRecord<string, unknown>Arbitrary metadata.

On this page