Tree

PreviousNext

A tree provides users with a way to navigate nested hierarchical information, with support for keyboard navigation and selection.

Documents
Budget
Photos
Family
import { Tree, TreeItem } from "@/components/ui/tree"

export function TreeDemo() {
  return (
    <Tree
      aria-label="Files"
      className="w-64"
      defaultExpandedKeys={["documents", "photos"]}
    >
      <TreeItem id="documents" title="Documents">
        <TreeItem id="project" title="Project">
          <TreeItem id="report" title="Weekly Report" />
          <TreeItem id="presentation" title="Presentation" />
        </TreeItem>
        <TreeItem id="budget" title="Budget" />
      </TreeItem>
      <TreeItem id="photos" title="Photos">
        <TreeItem id="vacation" title="Vacation">
          <TreeItem id="beach" title="Beach" />
          <TreeItem id="mountains" title="Mountains" />
        </TreeItem>
        <TreeItem id="family" title="Family" />
      </TreeItem>
      <TreeItem id="videos" title="Videos">
        <TreeItem id="tutorials" title="Tutorials" />
        <TreeItem id="recordings" title="Recordings" />
      </TreeItem>
    </Tree>
  )
}

Installation

pnpm dlx shadcn@latest add tree

Usage

import { Tree, TreeItem } from "@/components/ui/tree"
<Tree aria-label="Files" defaultExpandedKeys={["documents"]}>
  <TreeItem id="documents" title="Documents">
    <TreeItem id="report" title="Weekly Report" />
    <TreeItem id="budget" title="Budget" />
  </TreeItem>
  <TreeItem id="photos" title="Photos">
    <TreeItem id="vacation" title="Vacation" />
  </TreeItem>
</Tree>

Features

A tree displays hierarchical data with parent and child relationships. It provides rich keyboard navigation and supports various interaction patterns like selection and expandable/collapsible nodes.

  • Keyboard navigation – Navigate items with arrow keys, Home, End, Page Up, Page Down, etc.
  • Expandable nodes – Expand and collapse parent items with children.
  • Single or multiple selection – Support for both single and multiple item selection.
  • Disabled items – Individual items can be disabled.
  • Accessible – Follows the ARIA tree pattern, with support for keyboard navigation and screen readers.
  • Styleable – Hover, focus, expanded, and selection states are provided for styling.

Examples

Default

Documents
Budget
Photos
Family
import { Tree, TreeItem } from "@/components/ui/tree"

export function TreeDemo() {
  return (
    <Tree
      aria-label="Files"
      className="w-64"
      defaultExpandedKeys={["documents", "photos"]}
    >
      <TreeItem id="documents" title="Documents">
        <TreeItem id="project" title="Project">
          <TreeItem id="report" title="Weekly Report" />
          <TreeItem id="presentation" title="Presentation" />
        </TreeItem>
        <TreeItem id="budget" title="Budget" />
      </TreeItem>
      <TreeItem id="photos" title="Photos">
        <TreeItem id="vacation" title="Vacation">
          <TreeItem id="beach" title="Beach" />
          <TreeItem id="mountains" title="Mountains" />
        </TreeItem>
        <TreeItem id="family" title="Family" />
      </TreeItem>
      <TreeItem id="videos" title="Videos">
        <TreeItem id="tutorials" title="Tutorials" />
        <TreeItem id="recordings" title="Recordings" />
      </TreeItem>
    </Tree>
  )
}
<Tree
  aria-label="Files"
  className="w-64"
  defaultExpandedKeys={["documents", "photos"]}
>
  <TreeItem id="documents" title="Documents">
    <TreeItem id="project" title="Project">
      <TreeItem id="report" title="Weekly Report" />
      <TreeItem id="presentation" title="Presentation" />
    </TreeItem>
    <TreeItem id="budget" title="Budget" />
  </TreeItem>
  <TreeItem id="photos" title="Photos">
    <TreeItem id="vacation" title="Vacation">
      <TreeItem id="beach" title="Beach" />
      <TreeItem id="mountains" title="Mountains" />
    </TreeItem>
    <TreeItem id="family" title="Family" />
  </TreeItem>
</Tree>

Multiple Selection

Documents
Budget
Photos
Vacation
Beach
Mountains
Family
Selected: photos, vacation
"use client"

import { useState } from "react"

import { Tree, TreeItem } from "@/components/ui/tree"

export function TreeSelectionDemo() {
  const [selected, setSelected] = useState<Set<string>>(
    new Set(["photos", "vacation"])
  )

  return (
    <div className="flex flex-col gap-4">
      <Tree
        aria-label="Files"
        className="w-64"
        selectionMode="multiple"
        selectedKeys={selected}
        onSelectionChange={(keys) => setSelected(keys as Set<string>)}
        defaultExpandedKeys={["documents", "photos", "vacation"]}
      >
        <TreeItem id="documents" title="Documents">
          <TreeItem id="project" title="Project">
            <TreeItem id="report" title="Weekly Report" />
            <TreeItem id="presentation" title="Presentation" />
          </TreeItem>
          <TreeItem id="budget" title="Budget" />
        </TreeItem>
        <TreeItem id="photos" title="Photos">
          <TreeItem id="vacation" title="Vacation">
            <TreeItem id="beach" title="Beach" />
            <TreeItem id="mountains" title="Mountains" />
          </TreeItem>
          <TreeItem id="family" title="Family" />
        </TreeItem>
        <TreeItem id="videos" title="Videos">
          <TreeItem id="tutorials" title="Tutorials" />
        </TreeItem>
      </Tree>
      <div className="text-muted-foreground text-sm">
        Selected: {Array.from(selected).join(", ")}
      </div>
    </div>
  )
}
"use client"
 
import { useState } from "react"
import { Tree, TreeItem } from "@/components/ui/tree"
 
export default function Example() {
  const [selected, setSelected] = useState<Set<string>>(
    new Set(["photos", "vacation"])
  )
 
  return (
    <Tree
      aria-label="Files"
      selectionMode="multiple"
      selectedKeys={selected}
      onSelectionChange={(keys) => setSelected(keys as Set<string>)}
      defaultExpandedKeys={["documents", "photos"]}
    >
      <TreeItem id="documents" title="Documents">
        <TreeItem id="project" title="Project">
          <TreeItem id="report" title="Weekly Report" />
        </TreeItem>
      </TreeItem>
      <TreeItem id="photos" title="Photos">
        <TreeItem id="vacation" title="Vacation">
          <TreeItem id="beach" title="Beach" />
        </TreeItem>
      </TreeItem>
    </Tree>
  )
}

Set selectionMode to "multiple" to enable multiple selection. Selected items can be controlled with selectedKeys and onSelectionChange.

Single Selection

Documents
Budget
Selected: project
"use client"

import { useState } from "react"

import { Tree, TreeItem } from "@/components/ui/tree"

export function TreeSingleSelectionDemo() {
  const [selected, setSelected] = useState<"all" | Set<string>>(
    new Set(["project"])
  )

  return (
    <div className="flex flex-col gap-4">
      <Tree
        aria-label="Files"
        className="w-64"
        selectionMode="single"
        selectedKeys={selected}
        onSelectionChange={setSelected}
        defaultExpandedKeys={["documents"]}
      >
        <TreeItem id="documents" title="Documents">
          <TreeItem id="project" title="Project">
            <TreeItem id="report" title="Weekly Report" />
            <TreeItem id="presentation" title="Presentation" />
          </TreeItem>
          <TreeItem id="budget" title="Budget" />
        </TreeItem>
        <TreeItem id="photos" title="Photos">
          <TreeItem id="vacation" title="Vacation" />
          <TreeItem id="family" title="Family" />
        </TreeItem>
      </Tree>
      <div className="text-muted-foreground text-sm">
        Selected: {selected === "all" ? "all" : Array.from(selected).join(", ")}
      </div>
    </div>
  )
}
"use client"
 
import { useState } from "react"
import { Tree, TreeItem } from "@/components/ui/tree"
 
export default function Example() {
  const [selected, setSelected] = useState<"all" | Set<string>>(
    new Set(["project"])
  )
 
  return (
    <Tree
      aria-label="Files"
      selectionMode="single"
      selectedKeys={selected}
      onSelectionChange={setSelected}
      defaultExpandedKeys={["documents"]}
    >
      <TreeItem id="documents" title="Documents">
        <TreeItem id="project" title="Project" />
        <TreeItem id="budget" title="Budget" />
      </TreeItem>
      <TreeItem id="photos" title="Photos">
        <TreeItem id="vacation" title="Vacation" />
      </TreeItem>
    </Tree>
  )
}

Set selectionMode to "single" to allow only one item to be selected at a time.

Disabled Items

Documents
Budget (Disabled)
Photos
Vacation
Family (Disabled)
import { Tree, TreeItem } from "@/components/ui/tree"

export function TreeDisabledDemo() {
  return (
    <Tree
      aria-label="Files"
      className="w-64"
      selectionMode="multiple"
      disabledKeys={["budget", "family"]}
      defaultExpandedKeys={["documents", "photos"]}
    >
      <TreeItem id="documents" title="Documents">
        <TreeItem id="project" title="Project">
          <TreeItem id="report" title="Weekly Report" />
        </TreeItem>
        <TreeItem id="budget" title="Budget (Disabled)" />
      </TreeItem>
      <TreeItem id="photos" title="Photos">
        <TreeItem id="vacation" title="Vacation" />
        <TreeItem id="family" title="Family (Disabled)" />
      </TreeItem>
    </Tree>
  )
}
<Tree
  aria-label="Files"
  selectionMode="multiple"
  disabledKeys={["budget", "family"]}
  defaultExpandedKeys={["documents", "photos"]}
>
  <TreeItem id="documents" title="Documents">
    <TreeItem id="project" title="Project" />
    <TreeItem id="budget" title="Budget (Disabled)" />
  </TreeItem>
  <TreeItem id="photos" title="Photos">
    <TreeItem id="vacation" title="Vacation" />
    <TreeItem id="family" title="Family (Disabled)" />
  </TreeItem>
</Tree>

Use the disabledKeys prop to disable specific items in the tree. Disabled items cannot be selected or interacted with.

Components
Button
Input
Tree
Hooks
useState
useEffect
import { Tree, TreeItem } from "@/components/ui/tree"

export function TreeLinksDemo() {
  return (
    <Tree
      aria-label="Navigation"
      className="w-64"
      defaultExpandedKeys={["components", "hooks"]}
    >
      <TreeItem id="components" title="Components">
        <TreeItem id="button" title="Button" href="/docs/components/button" />
        <TreeItem id="input" title="Input" href="/docs/components/input" />
        <TreeItem id="tree" title="Tree" href="/docs/components/tree" />
      </TreeItem>
      <TreeItem id="hooks" title="Hooks">
        <TreeItem
          id="use-state"
          title="useState"
          href="/docs/hooks/use-state"
        />
        <TreeItem
          id="use-effect"
          title="useEffect"
          href="/docs/hooks/use-effect"
        />
      </TreeItem>
    </Tree>
  )
}
import { Tree, TreeItem } from "@/components/ui/tree"
 
<Tree
  aria-label="Navigation"
  defaultExpandedKeys={["components", "hooks"]}
>
  <TreeItem id="components" title="Components">
    <TreeItem id="button" title="Button" href="/docs/components/button" />
    <TreeItem id="input" title="Input" href="/docs/components/input" />
    <TreeItem id="tree" title="Tree" href="/docs/components/tree" />
  </TreeItem>
  <TreeItem id="hooks" title="Hooks">
    <TreeItem id="use-state" title="useState" href="/docs/hooks/use-state" />
    <TreeItem id="use-effect" title="useEffect" href="/docs/hooks/use-effect" />
  </TreeItem>
</Tree>

Tree items can be used as navigation links by providing an href prop. The title will be rendered as the link text.

Dynamic Content

Documents
Project
Weekly Report
Presentation
"use client"

import { useState } from "react"

import { Button } from "@/components/ui/button"
import { Tree, TreeItem } from "@/components/ui/tree"

interface FileNode {
  id: string
  title: string
  children?: FileNode[]
}

export function TreeDynamicDemo() {
  const [items, setItems] = useState<FileNode[]>([
    {
      id: "documents",
      title: "Documents",
      children: [
        {
          id: "project",
          title: "Project",
          children: [
            { id: "report", title: "Weekly Report" },
            { id: "presentation", title: "Presentation" },
          ],
        },
      ],
    },
    {
      id: "photos",
      title: "Photos",
      children: [{ id: "vacation", title: "Vacation" }],
    },
  ])

  const addItem = () => {
    setItems([
      ...items,
      {
        id: `item-${Date.now()}`,
        title: `New Folder ${items.length + 1}`,
      },
    ])
  }

  const renderItem = (item: FileNode): React.ReactElement => (
    <TreeItem key={item.id} id={item.id} title={item.title}>
      {item.children?.map(renderItem)}
    </TreeItem>
  )

  return (
    <div className="flex flex-col gap-4">
      <Button onClick={addItem} size="sm" variant="outline" className="w-fit">
        Add Folder
      </Button>
      <Tree
        aria-label="Files"
        className="w-64"
        defaultExpandedKeys={["documents", "project"]}
      >
        {items.map(renderItem)}
      </Tree>
    </div>
  )
}
"use client"
 
import { useState } from "react"
import { Button } from "@/components/ui/button"
import { Tree, TreeItem } from "@/components/ui/tree"
 
interface FileNode {
  id: string
  title: string
  children?: FileNode[]
}
 
export default function Example() {
  const [items, setItems] = useState<FileNode[]>([
    {
      id: "documents",
      title: "Documents",
      children: [
        { id: "report", title: "Weekly Report" },
      ],
    },
  ])
 
  const addItem = () => {
    setItems([
      ...items,
      {
        id: `item-${Date.now()}`,
        title: `New Folder ${items.length + 1}`,
      },
    ])
  }
 
  const renderItem = (item: FileNode): React.ReactElement => (
    <TreeItem key={item.id} id={item.id} title={item.title}>
      {item.children?.map(renderItem)}
    </TreeItem>
  )
 
  return (
    <div>
      <Button onClick={addItem}>Add Folder</Button>
      <Tree aria-label="Files" defaultExpandedKeys={["documents"]}>
        {items.map(renderItem)}
      </Tree>
    </div>
  )
}

You can dynamically add, remove, or modify tree items using React state.

API Reference

Tree

The Tree component displays hierarchical data with expandable/collapsible nodes.

PropTypeDefaultDescription
selectionMode"none" | "single" | "multiple""none"Whether the tree supports selection and what mode.
selectedKeysIterable<Key>-The currently selected keys (controlled).
defaultSelectedKeysIterable<Key>-The initial selected keys (uncontrolled).
onSelectionChange(keys: Selection) => void-Handler called when the selection changes.
expandedKeysIterable<Key>-The currently expanded keys (controlled).
defaultExpandedKeysIterable<Key>-The initial expanded keys (uncontrolled).
onExpandedChange(keys: Set<Key>) => void-Handler called when the expanded state changes.
disabledKeysIterable<Key>-A list of keys for items that are disabled.
aria-labelstring-An accessibility label for the tree (required).

TreeItem

Individual items within a tree. Tree items can be nested to create hierarchical structures.

PropTypeDefaultDescription
idKey-A unique key for the item (required).
titlestring-The text content for the item (required).
hrefstring-A URL to navigate to when the item is pressed.
childrenReact.ReactNode-Child tree items for creating nested structures.

Apart from the props above, the components support all props from the react-aria-components library. Check the React Aria documentation for more details.

Keyboard Navigation

Trees support extensive keyboard navigation to provide an accessible experience:

KeyAction
Arrow DownMoves focus to the next item.
Arrow UpMoves focus to the previous item.
Arrow RightExpands a collapsed item or moves to first child.
Arrow LeftCollapses an expanded item or moves to parent.
HomeMoves focus to the first item.
EndMoves focus to the last item.
Enter or SpaceSelects the focused item (if selection is enabled).
Ctrl/Cmd + ASelects all items (if multiple selection is enabled).

Accessibility

Trees follow the ARIA tree pattern, which provides keyboard navigation, selection announcements, and focus management.

  • Keyboard Navigation – Full keyboard support for navigating and interacting with the tree.
  • Selection – Selected state is properly announced to screen readers.
  • Expansion – Expanded/collapsed state is announced for parent items.
  • ARIA Attributes – Proper ARIA roles and attributes for tree navigation.

Always provide an aria-label to identify the tree to assistive technology:

<Tree aria-label="File system">
  {/* items */}
</Tree>
  • GridList - For flat lists with selection
  • ListBox - For simple option lists
  • Menu - For action menus