@usevyre/react @usevyre/vue

Tree

A hierarchical tree view for file explorers and nested navigation. Data-driven and controlled: pass a nested data array and the Tree renders recursively. A node with children is a folder; a leaf fires onSelect. Full keyboard support (/ move, / expand/collapse, Enter select).


File explorer

Click a folder to expand/collapse, a file to select. State is fully controlled — selectedId/onSelect and expandedIds (here seeded with defaultExpandedIds). README.md is disabled.

  • src
    • index.ts
    • components
      • Button.tsx
      • Tree.tsx
    • utils.ts
  • package.json
  • README.md

Selected: src/components/Tree.tsx

import { useState } from "react";
import { Tree } from "@usevyre/react";

const data = [
  { id: "src", label: "src", children: [
    { id: "src/index.ts", label: "index.ts" },
    { id: "src/components", label: "components", children: [
      { id: "src/components/Button.tsx", label: "Button.tsx" },
      { id: "src/components/Tree.tsx", label: "Tree.tsx" },
    ]},
    { id: "src/utils.ts", label: "utils.ts" },
  ]},
  { id: "package.json", label: "package.json" },
  { id: "README.md", label: "README.md", disabled: true },
];

const [selected, setSelected] = useState<string | null>(
  "src/components/Tree.tsx"
);

<Tree
  data={data}
  selectedId={selected}
  onSelect={setSelected}
  defaultExpandedIds={["src", "src/components"]}
/>

Props

Props

Prop Type Default Description
data TreeNode[] Nested { id, label, icon?, disabled?, children? }.
expandedIds string[] Controlled expanded node ids. Omit for uncontrolled.
defaultExpandedIds string[] [] Initially expanded ids when uncontrolled.
onExpandedChange (ids: string[]) => void Fires when a folder is expanded/collapsed.
selectedId string | null Controlled selected node id.
defaultSelectedId string | null null Initial selection when uncontrolled.
onSelect (id: string) => void Fires with the node id (not an event).
Data-driven, not composed. Tree takes a nested array, not nested children — so AI cannot mis-nest markup, and expansion/selection stay controlled state instead of leaking into the DOM.