Tree
Nested item tree in four shapes: app navigation with collapsible sections, a table of contents, an icon rail, and an editable file explorer. Every row is a Button; navigation flows through onNavigate.
Preview
Use the controls to explore the component's props.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "navigation" | "toc" | "sidebar" | "files" | — | navigation: app nav — 3-level data promotes the top level to collapsible section names. toc: top-level nodes are routes, deeper rows jump to #{id} after select; parents expand from an overlay chevron. sidebar: single-level icon rail that fills its container; collapsing folds labels to icon tiles. files: compact explorer with rename, drag-and-drop and trailing actions. |
items | ITreeNode[] | — | The tree data — id, label, icon, href (metadata for onNavigate; rows never render anchors, though toc rows below the route level still jump to #{id} or a #hash href), badge, defaultOpen, children. |
activeId | string | — | id of the current entry, any depth. Marks the row aria-current and expands its collapsed ancestors. |
onNavigate | (node: ITreeNode) => void | — | Fires for every selection after the node's own onSelect. Rows are buttons, so this is the routing path — wire your router's navigate here. toc rows below the route level additionally jump to #{id} after it fires. |
collapsed | boolean | false | sidebar only — folds rows to their leading icon; the label moves into aria-label and a hover/focus tooltip. |
onMove | (id, parentId: string | null, index: number) => void | — | files only — enables drag-and-drop. Reorder items yourself; the moveNode helper is exported. |
onRename | (id, label: string) => void | — | files only — enables inline rename (double-click or F2). |
renderTrailing | (node: ITreeNode) => ReactNode | — | files only — row actions revealed on hover/focus, e.g. a menu or delete button. Rendered beside the row, not inside it, so the content may be interactive; the row reserves ~36px for it, and a wider slot overlaps the truncated label. |
sound | boolean | true | Expand/collapse cues. Silent until bindSounds(). |
tree | ITreeController | — | A useTree() controller, in place of items and the handler props. Every behaviour — expand, collapse, select, rename, reorder, fold the rail — becomes callable from outside the component. |
treeId | string | — | Names this instance in the shared store so useTreeState(id) / useTreeActions(id) can reach it from anywhere under ExegiaProvider. A named tree keeps its state across unmounts (a rail's fold survives a route change) — call removeTreeInstance(id) on teardown. Unnamed trees are dropped on unmount. |
Example
import { Tree, useTree, useTreeActions, useTreeState } from "@exegia/corpora-ui"
// Props form — the tree owns its state.
<Tree
variant="navigation"
items={items}
activeId={pathnameId}
onNavigate={(node) => navigate(node.href!)}
/>
// Controller form — drive it from anywhere.
const tree = useTree({ variant: "files", defaultItems: files })
<Tree tree={tree} />
<Button onClick={tree.collapseAll}>Collapse all</Button>
<Button onClick={() => tree.startRename(tree.activeId!)}>Rename</Button>
// By id — no controller to pass around. Needs <ExegiaProvider> at the root.
<Tree variant="sidebar" treeId="app-nav" items={items} />
// …anywhere else in the app:
const nav = useTreeActions("app-nav") // writes only, never re-renders
const { collapsed } = useTreeState("app-nav") // subscribes to the tree
<Button onClick={nav.toggleCollapsed}>{collapsed ? "Expand" : "Fold"} rail</Button>Last updated on
Social Providers
Social auth buttons (Google, Apple, GitHub, X) in stacked or icon-row layout.
Logo
Brand lockup: a mark beside a wordmark. The mark is an SVG, an image, or a monogram tile derived from the name; variant="mark" folds the wordmark away with the same motion a collapsing rail uses. With href the whole lockup is a home link.