Getting Started
Foundations
Components
Search for a command to run...
A hierarchical list component that displays nested data in an expandable tree structure.
Tree views are used to display hierarchical data such as file systems, organizational charts, navigation menus, or any nested data structure. Users can expand and collapse parent nodes to reveal or hide their children, making it easy to navigate through complex nested information.
This is a composable component that provides full control over the tree structure. Use TreeItem, TreeItemTrigger, TreeItemIndicator, TreeItemIcon, TreeItemLabel, and TreeItemContent to build your tree.
Registry Component: This component is available from the Strongtie
registry. Add it using: npx shadcn@latest add @strongtie/tree
"use client"
import * as React from "react"
import {
Tree,
TreeItem,
TreeItemContent,
TreeItemIcon,
TreeItemIndicator,
TreeItemLabel,
TreeItemTrigger,
} from "@/components/ui/tree"
import type { TreeItemData } from "@/components/ui/tree"
const treeData: TreeItemData[] = [
{
id: "1",
name: "src",
type: "folder",
children: [
{
id: "2",
name: "components",
type: "folder",
children: [
{ id: "3", name: "Button.tsx", type: "file" },
{ id: "4", name: "Input.tsx", type: "file" },
{ id: "5", name: "Card.tsx", type: "file" },
],
},
{
id: "6",
name: "utils",
type: "folder",
children: [
{ id: "7", name: "helpers.ts", type: "file" },
{ id: "8", name: "constants.ts", type: "file" },
],
},
{ id: "9", name: "index.ts", type: "file" },
],
},
{
id: "10",
name: "public",
type: "folder",
children: [
{
id: "11",
name: "images",
type: "folder",
children: [{ id: "12", name: "logo.png", type: "file" }],
},
],
},
{ id: "13", name: "package.json", type: "file" },
{ id: "14", name: "README.md", type: "file" },
]
interface TreeDataItem extends TreeItemData {
children?: TreeDataItem[]
}
function renderTreeItems(items: TreeDataItem[]) {
return items.map((item) => (
<TreeItem key={item.id} id={item.id} value={item}>
<TreeItemTrigger>
<TreeItemIndicator />
<TreeItemIcon />
<TreeItemLabel>{item.name}</TreeItemLabel>
</TreeItemTrigger>
{item.children && (
<TreeItemContent>{renderTreeItems(item.children)}</TreeItemContent>
)}
</TreeItem>
))
}
export function TreeDefault() {
const [selected, setSelected] = React.useState<string>()
return (
<div className="w-full max-w-md rounded-md border p-4">
<Tree
selected={selected}
onSelect={(id) => {
setSelected(id)
console.log("Selected:", id)
}}
>
{renderTreeItems(treeData)}
</Tree>
</div>
)
}
Add this component from the Strongtie registry:
npx shadcn@latest add @strongtie/tree
Or by URL:
npx shadcn@latest add https://design.strongtie.io/r/tree.json
import { Tree } from "@/components/ui/tree"<TreeItemContent>| Prop | Type | Default | Description |
|---|---|---|---|
defaultExpanded | string[] | - | Array of item IDs that should be expanded by default |
expanded | string[] | - | Controlled array of expanded item IDs |
onExpandedChange | ((expanded: string[]) => void) | - | Callback when expanded items change |
onSelect | ((id: string, item: TreeItemData) => void) | - | Callback when an item is selected |
selected | string | - | ID of the currently selected item |
"use client"
import * as React from "react"
import {
Tree,
TreeItem,
TreeItemContent,
TreeItemIcon,
TreeItemIndicator,
TreeItemLabel,
TreeItemTrigger,
} from "@/components/ui/tree"
import type { TreeItemData } from "@/components/ui/tree"
const treeData: TreeItemData[] = [
{
id: "1",
name: "src",
type: "folder",
children: [
{
id: "2",
name: "components",
type: "folder",
children: [
{ id: "3", name: "Button.tsx", type: "file" },
{ id: "4", name: "Input.tsx", type: "file" },
{ id: "5", name: "Card.tsx", type: "file" },
],
},
{
id: "6",
name: "utils",
type: "folder",
children: [
{ id: "7", name: "helpers.ts", type: "file" },
{ id: "8", name: "constants.ts", type: "file" },
],
},
{ id: "9", name: "index.ts", type: "file" },
],
},
{
id: "10",
name: "public",
type: "folder",
children: [
{
id: "11",
name: "images",
type: "folder",
children: [{ id: "12", name: "logo.png", type: "file" }],
},
],
},
{ id: "13", name: "package.json", type: "file" },
{ id: "14", name: "README.md", type: "file" },
]
interface TreeDataItem extends TreeItemData {
children?: TreeDataItem[]
}
function renderTreeItems(items: TreeDataItem[]) {
return items.map((item) => (
<TreeItem key={item.id} id={item.id} value={item}>
<TreeItemTrigger>
<TreeItemIndicator />
<TreeItemIcon />
<TreeItemLabel>{item.name}</TreeItemLabel>
</TreeItemTrigger>
{item.children && (
<TreeItemContent>{renderTreeItems(item.children)}</TreeItemContent>
)}
</TreeItem>
))
}
export function TreeDefault() {
const [selected, setSelected] = React.useState<string>()
return (
<div className="w-full max-w-md rounded-md border p-4">
<Tree
selected={selected}
onSelect={(id) => {
setSelected(id)
console.log("Selected:", id)
}}
>
{renderTreeItems(treeData)}
</Tree>
</div>
)
}
"use client"
import * as React from "react"
import {
Tree,
TreeItem,
TreeItemContent,
TreeItemIcon,
TreeItemIndicator,
TreeItemLabel,
TreeItemTrigger,
} from "@/components/ui/tree"
import {
Activity,
Calendar,
Cpu,
Database,
HardDrive,
Hash,
Key,
Link,
ListTree,
Lock,
Server,
Settings,
Shield,
Table,
ToggleLeft,
Type,
Users,
} from "lucide-react"
export function TreeCustomIcons() {
const [selected, setSelected] = React.useState<string>()
return (
<div className="w-full max-w-lg rounded-md border p-4">
<Tree
selected={selected}
onSelect={(id) => setSelected(id)}
defaultExpanded={["db", "users-table", "infrastructure"]}
>
{/* Database Section */}
<TreeItem id="db" value={{ id: "db", name: "Production Database" }}>
<TreeItemTrigger>
<TreeItemIndicator />
<TreeItemIcon>
<Database className="size-4 text-blue-500" />
</TreeItemIcon>
<TreeItemLabel>Production Database</TreeItemLabel>
</TreeItemTrigger>
<TreeItemContent>
{/* Users Table */}
<TreeItem
id="users-table"
value={{ id: "users-table", name: "users" }}
>
<TreeItemTrigger>
<TreeItemIndicator />
<TreeItemIcon>
<Table className="size-4 text-emerald-500" />
</TreeItemIcon>
<TreeItemLabel>users</TreeItemLabel>
</TreeItemTrigger>
<TreeItemContent>
<TreeItem id="users-id" value={{ id: "users-id", name: "id" }}>
<TreeItemTrigger>
<TreeItemIndicator />
<TreeItemIcon>
<Key className="size-4 text-amber-500" />
</TreeItemIcon>
<TreeItemLabel>id (primary key)</TreeItemLabel>
</TreeItemTrigger>
</TreeItem>
<TreeItem
id="users-email"
value={{ id: "users-email", name: "email" }}
>
<TreeItemTrigger>
<TreeItemIndicator />
<TreeItemIcon>
<Type className="size-4 text-purple-500" />
</TreeItemIcon>
<TreeItemLabel>email (varchar)</TreeItemLabel>
</TreeItemTrigger>
</TreeItem>
<TreeItem
id="users-created"
value={{ id: "users-created", name: "created_at" }}
>
<TreeItemTrigger>
<TreeItemIndicator />
<TreeItemIcon>
<Calendar className="size-4 text-rose-500" />
</TreeItemIcon>
<TreeItemLabel>created_at (timestamp)</TreeItemLabel>
</TreeItemTrigger>
</TreeItem>
<TreeItem
id="users-active"
value={{ id: "users-active", name: "is_active" }}
>
<TreeItemTrigger>
<TreeItemIndicator />
<TreeItemIcon>
<ToggleLeft className="size-4 text-cyan-500" />
</TreeItemIcon>
<TreeItemLabel>is_active (boolean)</TreeItemLabel>
</TreeItemTrigger>
</TreeItem>
</TreeItemContent>
</TreeItem>
{/* Posts Table */}
<TreeItem
id="posts-table"
value={{ id: "posts-table", name: "posts" }}
>
<TreeItemTrigger>
<TreeItemIndicator />
<TreeItemIcon>
<Table className="size-4 text-emerald-500" />
</TreeItemIcon>
<TreeItemLabel>posts</TreeItemLabel>
</TreeItemTrigger>
<TreeItemContent>
<TreeItem id="posts-id" value={{ id: "posts-id", name: "id" }}>
<TreeItemTrigger>
<TreeItemIndicator />
<TreeItemIcon>
<Key className="size-4 text-amber-500" />
</TreeItemIcon>
<TreeItemLabel>id (primary key)</TreeItemLabel>
</TreeItemTrigger>
</TreeItem>
<TreeItem
id="posts-user"
value={{ id: "posts-user", name: "user_id" }}
>
<TreeItemTrigger>
<TreeItemIndicator />
<TreeItemIcon>
<Link className="size-4 text-orange-500" />
</TreeItemIcon>
<TreeItemLabel>user_id (foreign key)</TreeItemLabel>
</TreeItemTrigger>
</TreeItem>
<TreeItem
id="posts-title"
value={{ id: "posts-title", name: "title" }}
>
<TreeItemTrigger>
<TreeItemIndicator />
<TreeItemIcon>
<Type className="size-4 text-purple-500" />
</TreeItemIcon>
<TreeItemLabel>title (varchar)</TreeItemLabel>
</TreeItemTrigger>
</TreeItem>
</TreeItemContent>
</TreeItem>
{/* Indexes */}
<TreeItem id="indexes" value={{ id: "indexes", name: "Indexes" }}>
<TreeItemTrigger>
<TreeItemIndicator />
<TreeItemIcon>
<ListTree className="size-4 text-indigo-500" />
</TreeItemIcon>
<TreeItemLabel>Indexes</TreeItemLabel>
</TreeItemTrigger>
<TreeItemContent>
<TreeItem
id="idx-email"
value={{ id: "idx-email", name: "idx_users_email" }}
>
<TreeItemTrigger>
<TreeItemIndicator />
<TreeItemIcon>
<Hash className="size-4 text-gray-500" />
</TreeItemIcon>
<TreeItemLabel>idx_users_email</TreeItemLabel>
</TreeItemTrigger>
</TreeItem>
<TreeItem
id="idx-created"
value={{ id: "idx-created", name: "idx_posts_created" }}
>
<TreeItemTrigger>
<TreeItemIndicator />
<TreeItemIcon>
<Hash className="size-4 text-gray-500" />
</TreeItemIcon>
<TreeItemLabel>idx_posts_created</TreeItemLabel>
</TreeItemTrigger>
</TreeItem>
</TreeItemContent>
</TreeItem>
</TreeItemContent>
</TreeItem>
{/* Infrastructure Section */}
<TreeItem
id="infrastructure"
value={{ id: "infrastructure", name: "Infrastructure" }}
>
<TreeItemTrigger>
<TreeItemIndicator />
<TreeItemIcon>
<Server className="size-4 text-slate-600" />
</TreeItemIcon>
<TreeItemLabel>Infrastructure</TreeItemLabel>
</TreeItemTrigger>
<TreeItemContent>
<TreeItem id="storage" value={{ id: "storage", name: "Storage" }}>
<TreeItemTrigger>
<TreeItemIndicator />
<TreeItemIcon>
<HardDrive className="size-4 text-blue-400" />
</TreeItemIcon>
<TreeItemLabel>Storage (256GB SSD)</TreeItemLabel>
</TreeItemTrigger>
</TreeItem>
<TreeItem id="compute" value={{ id: "compute", name: "Compute" }}>
<TreeItemTrigger>
<TreeItemIndicator />
<TreeItemIcon>
<Cpu className="size-4 text-green-500" />
</TreeItemIcon>
<TreeItemLabel>Compute (4 vCPUs)</TreeItemLabel>
</TreeItemTrigger>
</TreeItem>
<TreeItem
id="monitoring"
value={{ id: "monitoring", name: "Monitoring" }}
>
<TreeItemTrigger>
<TreeItemIndicator />
<TreeItemIcon>
<Activity className="size-4 text-red-500" />
</TreeItemIcon>
<TreeItemLabel>Monitoring</TreeItemLabel>
</TreeItemTrigger>
</TreeItem>
</TreeItemContent>
</TreeItem>
{/* Settings Section */}
<TreeItem id="settings" value={{ id: "settings", name: "Settings" }}>
<TreeItemTrigger>
<TreeItemIndicator />
<TreeItemIcon>
<Settings className="size-4 text-gray-500" />
</TreeItemIcon>
<TreeItemLabel>Settings</TreeItemLabel>
</TreeItemTrigger>
<TreeItemContent>
<TreeItem
id="security"
value={{ id: "security", name: "Security" }}
>
<TreeItemTrigger>
<TreeItemIndicator />
<TreeItemIcon>
<Shield className="size-4 text-green-600" />
</TreeItemIcon>
<TreeItemLabel>Security</TreeItemLabel>
</TreeItemTrigger>
</TreeItem>
<TreeItem
id="access"
value={{ id: "access", name: "Access Control" }}
>
<TreeItemTrigger>
<TreeItemIndicator />
<TreeItemIcon>
<Users className="size-4 text-blue-600" />
</TreeItemIcon>
<TreeItemLabel>Access Control</TreeItemLabel>
</TreeItemTrigger>
</TreeItem>
<TreeItem
id="encryption"
value={{ id: "encryption", name: "Encryption" }}
>
<TreeItemTrigger>
<TreeItemIndicator />
<TreeItemIcon>
<Lock className="size-4 text-amber-600" />
</TreeItemIcon>
<TreeItemLabel>Encryption</TreeItemLabel>
</TreeItemTrigger>
</TreeItem>
</TreeItemContent>
</TreeItem>
</Tree>
</div>
)
}