Skip to main content

Tree

Hierarchical data with expandable nodes, selection and keyboard navigation.

src
components
button.tsx
card.tsx
tree.tsx
index.ts
package.json
tsconfig.json
1'use client';
2
3import { Tree } from '@/components/ui/tree';
4
5export function Default() {
6 return (
7 <Tree defaultExpanded={['src', 'src/components']} className="max-w-xs">
8 <Tree.Item value="src" label="src">
9 <Tree.Item value="src/components" label="components">
10 <Tree.Item value="src/components/button.tsx" label="button.tsx" />
11 <Tree.Item value="src/components/card.tsx" label="card.tsx" />
12 <Tree.Item value="src/components/tree.tsx" label="tree.tsx" />
13 </Tree.Item>
14 <Tree.Item value="src/hooks" label="hooks">
15 <Tree.Item value="src/hooks/use-local-storage.ts" label="use-local-storage.ts" />
16 </Tree.Item>
17 <Tree.Item value="src/index.ts" label="index.ts" />
18 </Tree.Item>
19 <Tree.Item value="package.json" label="package.json" />
20 <Tree.Item value="tsconfig.json" label="tsconfig.json" />
21 </Tree>
22 );
23}

Installation

pnpm dlx nachui add tree

Anatomy

1import { Tree } from '@/components/ui/tree';
1<Tree defaultExpanded={['src']}>
2 <Tree.Item value="src" label="src">
3 <Tree.Item value="src/index.ts" label="index.ts" />
4 </Tree.Item>
5 <Tree.Item value="package.json" label="package.json" />
6</Tree>
An item with children is a branch: clicking it toggles expansion. An item without children is a leaf: clicking it selects it and fires onSelectedChange on the root. Arrow keys walk the visible rows, ArrowRight expands, ArrowLeft collapses or jumps to the parent, Home and End jump to the edges.

Composition

Use the following composition to build a Tree:
Tree
└── Tree.Item
└── Tree.Item

Variants

Lines

showLines draws a guide line along each level.
app
docs
elements
page.tsx
layout.tsx
page.tsx
layout.tsx
globals.css
proxy.ts
1'use client';
2
3import { Tree } from '@/components/ui/tree';
4
5export function Lines() {
6 return (
7 <Tree showLines defaultExpanded={['app', 'app/docs', 'app/docs/elements']} className="max-w-xs">
8 <Tree.Item value="app" label="app">
9 <Tree.Item value="app/docs" label="docs">
10 <Tree.Item value="app/docs/elements" label="elements">
11 <Tree.Item value="app/docs/elements/page.tsx" label="page.tsx" />
12 <Tree.Item value="app/docs/elements/layout.tsx" label="layout.tsx" />
13 </Tree.Item>
14 <Tree.Item value="app/docs/page.tsx" label="page.tsx" />
15 </Tree.Item>
16 <Tree.Item value="app/layout.tsx" label="layout.tsx" />
17 <Tree.Item value="app/globals.css" label="globals.css" />
18 </Tree.Item>
19 <Tree.Item value="proxy.ts" label="proxy.ts" />
20 </Tree>
21 );
22}

Icons

Pass icon for every node and iconOpen for branches that change while expanded, like folders.
packages
ui
tree.tsx
frame.tsx
turbo.json
1'use client';
2
3import { File01Icon, Folder01Icon, Folder02Icon } from '@hugeicons/core-free-icons';
4import { HugeiconsIcon } from '@hugeicons/react';
5import { Tree } from '@/components/ui/tree';
6
7const folderClosed = <HugeiconsIcon icon={Folder01Icon} size={16} />;
8const folderOpen = <HugeiconsIcon icon={Folder02Icon} size={16} />;
9const file = <HugeiconsIcon icon={File01Icon} size={16} />;
10
11export function Icons() {
12 return (
13 <Tree defaultExpanded={['packages', 'packages/ui']} className="max-w-xs">
14 <Tree.Item value="packages" label="packages" icon={folderClosed} iconOpen={folderOpen}>
15 <Tree.Item value="packages/ui" label="ui" icon={folderClosed} iconOpen={folderOpen}>
16 <Tree.Item value="packages/ui/tree.tsx" label="tree.tsx" icon={file} />
17 <Tree.Item value="packages/ui/frame.tsx" label="frame.tsx" icon={file} />
18 </Tree.Item>
19 <Tree.Item value="packages/cli" label="cli" icon={folderClosed} iconOpen={folderOpen}>
20 <Tree.Item value="packages/cli/index.ts" label="index.ts" icon={file} />
21 </Tree.Item>
22 </Tree.Item>
23 <Tree.Item value="turbo.json" label="turbo.json" icon={file} />
24 </Tree>
25 );
26}

Plus and minus

toggleIcon="plus-minus" swaps the chevron for plus and minus toggles.
content
docs
README.md
1'use client';
2
3import { File01Icon, Folder01Icon, Folder02Icon } from '@hugeicons/core-free-icons';
4import { HugeiconsIcon } from '@hugeicons/react';
5import { Tree } from '@/components/ui/tree';
6
7const folderClosed = <HugeiconsIcon icon={Folder01Icon} size={16} />;
8const folderOpen = <HugeiconsIcon icon={Folder02Icon} size={16} />;
9const file = <HugeiconsIcon icon={File01Icon} size={16} />;
10
11export function PlusMinus() {
12 return (
13 <Tree
14 toggleIcon="plus-minus"
15 showLines
16 defaultExpanded={['content', 'content/docs']}
17 className="max-w-xs"
18 >
19 <Tree.Item value="content" label="content" icon={folderClosed} iconOpen={folderOpen}>
20 <Tree.Item value="content/docs" label="docs" icon={folderClosed} iconOpen={folderOpen}>
21 <Tree.Item value="content/docs/en" label="en" icon={folderClosed} iconOpen={folderOpen}>
22 <Tree.Item value="content/docs/en/tree.mdx" label="tree.mdx" icon={file} />
23 </Tree.Item>
24 <Tree.Item value="content/docs/es" label="es" icon={folderClosed} iconOpen={folderOpen}>
25 <Tree.Item value="content/docs/es/tree.mdx" label="tree.mdx" icon={file} />
26 </Tree.Item>
27 </Tree.Item>
28 </Tree.Item>
29 <Tree.Item value="README.md" label="README.md" icon={file} />
30 </Tree>
31 );
32}

API Reference

Tree

PropTypeDefaultDescription
defaultExpandedstring[][]Branch values expanded on mount
defaultSelectedstring-Initially selected leaf
selectedstring-Controlled selected leaf
onSelectedChange(value: string) => void-Callback when a leaf is selected
indentnumber16Pixels of indentation per level
toggleIcon'chevron' | 'plus-minus''chevron'Expand and collapse icon style
showLinesbooleanfalseDraw guide lines between levels
labelstring'Tree'Accessible name for the tree
classNamestring-Additional CSS classes

Tree.Item

PropTypeDefaultDescription
valuestring-Unique value, used for expansion and selection
labelReactNode-Row content
iconReactNode-Leading icon
iconOpenReactNode-Icon while the branch is expanded
disabledbooleanfalseDisable the row
onSelect() => void-Callback when this leaf is selected
classNamestring-Additional CSS classes
Found something to improve?

Notice a bug, typo, or missing detail on this page? Help us make the documentation better by opening a GitHub issue.

Create an Issue
Ctrl+I