Component

A hierarchical data display component. Nest Tree.Item elements to represent folder structures, file trees, or any recursive data.

View component changelog
preview
React
"use client" import Tree from "@radui/ui/Tree" const items = [ { label: 'src', expanded: true, items: [ { label: 'components', expanded: false, items: [ { label: 'Button.tsx', expanded: false }, { label: 'Input.tsx', expanded: false } ] }, { label: 'utils', expanded: false, items: [ { label: 'helpers.ts', expanded: false } ] }, { label: 'index.ts', expanded: false } ] }, { label: 'package.json', expanded: false }, { label: 'tsconfig.json', expanded: false } ] const TreeExample = () => { return ( <Tree.Root> {items.map((item) => ( <Tree.Item key={item.label} item={item}> {item.label} </Tree.Item> ))} </Tree.Root> ) } export default TreeExample

Model large trees as collapsed branches and keep each node's label stable. Tree only mounts a branch group while its parent item is expanded, so deep file trees can defer most child nodes until users open that branch.

React
"use client" import * as React from "react" import Tree from "@radui/ui/Tree" type FileNode = { label: string expanded?: boolean items?: FileNode[] } const createFiles = (folder: string, count: number): FileNode[] => ( Array.from({ length: count }, (_, index) => ({ label: `${folder}-file-${String(index + 1).padStart(3, "0")}.tsx` })) ) const folders: FileNode[] = [ { label: "packages", expanded: true, items: [ { label: "core", items: createFiles("core", 80) }, { label: "react", items: createFiles("react", 120) }, { label: "theme", items: createFiles("theme", 64) } ] }, { label: "docs", items: [ { label: "components", items: createFiles("component-doc", 96) }, { label: "guides", items: createFiles("guide", 48) } ] }, { label: "examples", items: createFiles("example", 72) } ] const LazyBranchTreeExample = () => { const [selectedPath, setSelectedPath] = React.useState("packages") const getIsSelected = React.useCallback((item: FileNode) => ( item.label === selectedPath ), [selectedPath]) return ( <Tree.Root aria-label="Project files" loop={false}> {folders.map((item) => ( <Tree.Item key={item.label} item={item} getIsSelected={getIsSelected} onToggleSelect={(_, selectedItem) => { setSelectedPath(selectedItem.label) }} > {item.label} </Tree.Item> ))} </Tree.Root> ) } export default LazyBranchTreeExample
ShortcutDescription
Tab

Moves focus into the tree and onto the current roving tree item.

ArrowDown

Moves focus to the next visible tree item. With loop enabled, focus wraps from the last visible item to the first.

ArrowUp

Moves focus to the previous visible tree item. With loop enabled, focus wraps from the first visible item to the last.

ArrowRight

Expands a collapsed parent item. When the parent is already expanded, moves focus to its first child.

ArrowLeft

Collapses an expanded parent item. From a collapsed child or leaf item, moves focus back to the parent item.

Home

Moves focus to the first visible tree item.

End

Moves focus to the last visible tree item.

Enter

Runs onToggleSelect for the focused item when selection is controlled by the consumer.

ReferenceWhat Rad UI follows

Tree.Root renders role="tree"; each Tree.Item renders role="treeitem", parent branches render role="group", expandable parents expose aria-expanded, and consumer-managed selection is reflected with aria-selected.

Import all parts of the component and piece them together

React
import Tree from "@radui/ui/Tree" const item = { label: 'Parent', expanded: true, items: [{ label: 'Child', expanded: false }] } export default () => { return ( <Tree.Root> <Tree.Item item={item}> {item.label} </Tree.Item> </Tree.Root> ) }

A single tree node rendered from an item object.

PropTypeDefault
item*
{ label: string; expanded?: boolean; items?: object[] }
--
level
number
0
isSelected
boolean
false
getIsSelected
(item) => boolean
--
onToggleSelect
(id, item) => void
--
className
string
""