Skip to content

ComponentsCollections

Tree

An expandable hierarchy such as a file explorer, docs navigation or nested permissions. Items take a title, an icon that can follow the expanded state and a trailing suffix, with single or multiple selection and full keyboard support.

React AriaSource
app
layout.tsx
page.tsx
components
site-header.tsx
package.json
README.md

Installation

pnpm dlx shadcn@latest add @desyne/tree

The CLI installs dependencies and any other components this one uses.

Usage

tsx
import { Tree, TreeItem } from "@/components/ui/tree";
tsx
<Tree aria-label="Files" defaultExpandedKeys={["src"]}>
  <TreeItem id="src" title="src" icon={<FolderIcon />}>
    <TreeItem id="index" title="index.ts" icon={<FileIcon />} />
  </TreeItem>
  <TreeItem id="readme" title="README.md" icon={<FileIcon />} />
</Tree>

Nest TreeItems inside a TreeItem to give it children; the expand chevron appears automatically. Give every item a unique id.

When to use

  • Tree — data with real nesting that users open and close: folders, org charts, category trees, nested settings or permissions.
  • Disclosure — a few collapsible sections of content, not a navigable list of items.
  • Sidebar — app navigation with one or two levels. Use a tree inside it when the hierarchy is deep or user-defined (projects and folders).
  • Grid List or List Box — a flat list.

Anatomy

tsx
<Tree>                          {/* role="treegrid" */}
  <TreeItem>                    {/* role="row" aria-level aria-expanded */}
    {/* checkbox (toggle selection) · indent · chevron · icon · title · suffix */}
    <TreeItem />                {/* nested items */}
  </TreeItem>
</Tree>
PartRendersNotes
Tree<div role="treegrid">React Aria Tree. Owns expansion and selection state. variant="bordered" frames it as a card.
TreeItem<div role="row">One node. Sets aria-level, aria-expanded and aria-selected.
Selection checkboxCheckboxShown with selectionMode and the default selectionBehavior="toggle".
Indent<span>Width follows React Aria's --tree-item-level variable (16px per level).
Chevron<button slot="chevron">Only on items with children. Rotates when expanded. Not a tab stop; the row handles the keyboard.
Icon<span>icon: a node, or a function of the item state (for open/closed folders).
Suffix<span>suffix: counts, sizes, badges or a status dot, pushed to the end.

Examples

Dynamic items

Pass data to items and a render function as children. For nested data, render a Collection of the node's children inside each TreeItem, calling the same function recursively. React Aria only renders expanded branches.

Brand
logo.svg4 KB
logo@2x.png88 KB
Roadmap 2027.key18 MB
tsx
<Tree aria-label="Files" items={files}>
  {function renderNode(node) {
    return (
      <TreeItem id={node.id} title={node.name}>
        {node.children && <Collection items={node.children}>{renderNode}</Collection>}
      </TreeItem>
    );
  }}
</Tree>

Multiple selection

selectionMode="multiple" adds a checkbox to each row. Selecting a parent doesn't select its children; React Aria treats each row on its own, so select descendants yourself in onSelectionChange if your data needs it. isDisabled items can't be selected or expanded.

Repository
Read contents
Push commits
Manage settings
Issues
Read issues
Create and comment

3 permissions selected. Repository admin needs an owner role.

For a docs or project navigator, use selectionMode="single" with selectionBehavior="replace": clicking a row selects it with no checkbox, and the selected row shows the current page. disallowEmptySelection keeps one page selected. For links, give items an href instead and React Aria navigates on press (use your router's RouterProvider for client-side navigation).

Getting started
Installation
Theming
Guides
Forms
Dark mode
Next.js
API reference

Controlled expansion

expandedKeys and onExpandedChange control which items are open, for "Expand all" and "Collapse all" buttons or to remember the open folders. suffix holds an unread count here.

General
announcements
random4

Accessibility

  • The tree is a treegrid: one tab stop, with arrow keys inside. Give it an aria-label or aria-labelledby.
  • Each row announces its level, position ("3 of 5"), expanded state and selection.
  • title is the row's accessible name when it's a string. If title is JSX, pass textValue so type-ahead and screen readers get plain text.
  • The chevron and the selection checkbox are inside the row and aren't separate tab stops.
  • Icons are decorative; put the meaning in the title (or textValue), not only in a folder or lock icon.
  • Drag and drop isn't wired up in this component. React Aria's useDragAndDrop works with Tree; add a slot="drag" button to the item content if you need it.

Keyboard

KeyAction
↓ / ↑Next / previous visible row
→Expands a collapsed row; on an expanded row, moves to its first child
←Collapses an expanded row; on a child, moves to its parent
Home / EndFirst / last visible row
EnterExpands or collapses, or runs onAction / follows href
SpaceToggles selection
LettersType-ahead to a row by its text

Styling

Data attributes

On each TreeItem:

AttributePresent when
data-expandedThe item is expanded
data-has-child-itemsThe item has children
data-levelAlways: the 1-based depth
data-selectedThe item is selected
data-focused / data-focus-visibleThe row has focus / keyboard focus
data-hovered / data-pressedHovered / being pressed
data-disabledisDisabled is set

Items set --tree-item-level, which the indent uses. Change the step by editing the indent span's width in the source.

Data slots

SlotElement
data-slot="tree"Root (with data-variant)
data-slot="tree-item"A row
data-slot="tree-item-content"The row's content
data-slot="tree-item-icon"The icon
data-slot="tree-item-title"The title
data-slot="tree-item-suffix"The suffix

API Reference

Tree

Prop

Type

Also accepts every prop of React Aria's Tree.

TreeItem

Prop

Type

Also accepts every prop of React Aria's TreeItem.

  • Grid List — a flat interactive list with the same row styles.
  • Sidebar — app navigation that can host a tree.
  • Disclosure — collapsible content sections.