- Alert
- Avatar
- Badge
- Breadcrumb
- Button
- Button Group
- Calendar
- Card
- Carousel
- Chart
- Checkbox
- Checkbox Group
- ComboBox
- Command
- Data Table
- Date Field
- Date Picker
- Date Range Picker
- Dialog
- Disclosure
- Disclosure Group
- Drawer
- Empty
- Field
- File Trigger
- Form
- Grid List
- Hover Card
- Input
- Input Group
- Input OTP
- Item
- Kbd
- Label
- ListBox
- Menu
- Number Field
- Pagination
- Popover
- Progress Bar
- Radio Group
- Range Calendar
- Resizable
- Search Field
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Sonner
- Spinner
- Switch
- Table
- Tabs
- Tag Group
- Text Field
- Textarea
- Time Field
- Toast
- Toggle Button
- Toggle Button Group
- Tooltip
- Tree
- Typography
A tree provides users with a way to navigate nested hierarchical information, with support for keyboard navigation and selection.
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
pnpmnpmyarnbunpnpm 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
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
"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
"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
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.
Links
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
"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.
| Prop | Type | Default | Description |
|---|---|---|---|
selectionMode | "none" | "single" | "multiple" | "none" | Whether the tree supports selection and what mode. |
selectedKeys | Iterable<Key> | - | The currently selected keys (controlled). |
defaultSelectedKeys | Iterable<Key> | - | The initial selected keys (uncontrolled). |
onSelectionChange | (keys: Selection) => void | - | Handler called when the selection changes. |
expandedKeys | Iterable<Key> | - | The currently expanded keys (controlled). |
defaultExpandedKeys | Iterable<Key> | - | The initial expanded keys (uncontrolled). |
onExpandedChange | (keys: Set<Key>) => void | - | Handler called when the expanded state changes. |
disabledKeys | Iterable<Key> | - | A list of keys for items that are disabled. |
aria-label | string | - | An accessibility label for the tree (required). |
TreeItem
Individual items within a tree. Tree items can be nested to create hierarchical structures.
| Prop | Type | Default | Description |
|---|---|---|---|
id | Key | - | A unique key for the item (required). |
title | string | - | The text content for the item (required). |
href | string | - | A URL to navigate to when the item is pressed. |
children | React.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:
| Key | Action |
|---|---|
Arrow Down | Moves focus to the next item. |
Arrow Up | Moves focus to the previous item. |
Arrow Right | Expands a collapsed item or moves to first child. |
Arrow Left | Collapses an expanded item or moves to parent. |
Home | Moves focus to the first item. |
End | Moves focus to the last item. |
Enter or Space | Selects the focused item (if selection is enabled). |
Ctrl/Cmd + A | Selects 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>