Tree
'use client'
import { TreeView, createTreeCollection } from '@chakra-ui/react'
import { LuFile, LuFolder } from 'react-icons/lu'
export const TreeViewBasic = () => {
return (
<TreeView.Root collection={collection} maxW="sm">
<TreeView.Label>Tree</TreeView.Label>
<TreeView.Tree>
<TreeView.Node
indentGuide={<TreeView.BranchIndentGuide />}
render={({ node, nodeState }) =>
nodeState.isBranch ? (
<TreeView.BranchControl>
<LuFolder />
<TreeView.BranchText>{node.name}</TreeView.BranchText>
</TreeView.BranchControl>
) : (
<TreeView.Item>
<LuFile />
<TreeView.ItemText>{node.name}</TreeView.ItemText>
</TreeView.Item>
)
}
/>
</TreeView.Tree>
</TreeView.Root>
)
}
interface Node {
id: string
name: string
children?: Node[]
}
const collection = createTreeCollection<Node>({
nodeToValue: (node) => node.id,
nodeToString: (node) => node.name,
rootNode: {
id: 'ROOT',
name: '',
children: [
{
id: 'node_modules',
name: 'node_modules',
children: [
{ id: 'node_modules/zag-js', name: 'zag-js' },
{ id: 'node_modules/pandacss', name: 'panda' },
{
id: 'node_modules/@types',
name: '@types',
children: [
{ id: 'node_modules/@types/react', name: 'react' },
{ id: 'node_modules/@types/react-dom', name: 'react-dom' },
],
},
],
},
{
id: 'src',
name: 'src',
children: [
{ id: 'src/app.tsx', name: 'app.tsx' },
{ id: 'src/index.ts', name: 'index.ts' },
],
},
{ id: 'panda.config', name: 'panda.config.ts' },
{ id: 'package.json', name: 'package.json' },
{ id: 'renovate.json', name: 'renovate.json' },
{ id: 'readme.md', name: 'README.md' },
],
},
})
Anatomy
import { TreeView, createTreeCollection } from '@chakra-ui/react'<TreeView.Root collection={collection}>
<TreeView.Label />
<TreeView.Tree>
<TreeView.Branch>
<TreeView.BranchControl>
<TreeView.BranchIndicator />
<TreeView.BranchText />
</TreeView.BranchControl>
<TreeView.BranchContent>
<TreeView.BranchIndentGuide />
<TreeView.Item />
</TreeView.BranchContent>
</TreeView.Branch>
<TreeView.Item />
</TreeView.Tree>
</TreeView.Root>Use the createTreeCollection function to register the tree data before
rendering the tree view.
Shortcuts
TreeView.Node
This component is a helper that manages the recursive rendering of the branch and leaf nodes for you.
<TreeView.Node
indentGuide={<TreeView.BranchIndentGuide />}
render={({ node, nodeState }) =>
nodeState.isBranch ? (
<TreeView.BranchControl>
<TreeView.BranchText>{node.name}</TreeView.BranchText>
</TreeView.BranchControl>
) : (
<TreeView.Item>
<TreeView.ItemText>{node.name}</TreeView.ItemText>
</TreeView.Item>
)
}
/>is equivalent to:
const TreeNode = (props) => {
const { node, indexPath } = props
return (
<TreeView.NodeProvider key={node.id} node={node} indexPath={indexPath}>
{node.children ? (
<TreeView.Branch>
<TreeView.BranchControl>
<TreeView.BranchText>{node.name}</TreeView.BranchText>
</TreeView.BranchControl>
<TreeView.BranchContent>
<TreeView.BranchIndentGuide />
{node.children.map((child, index) => (
<TreeNode
key={child.id}
node={child}
indexPath={[...indexPath, index]}
/>
))}
</TreeView.BranchContent>
</TreeView.Branch>
) : (
<TreeView.Item>
<TreeView.ItemText>{node.name}</TreeView.ItemText>
</TreeView.Item>
)}
</TreeView.NodeProvider>
)
}Examples
Sizes
Use the size prop to change the size of the tree view.
Tree (size=xs)
Tree (size=sm)
Tree (size=md)
'use client'
import { For, Stack, TreeView, createTreeCollection } from '@chakra-ui/react'
import { LuFile, LuFolder } from 'react-icons/lu'
export const TreeViewWithSizes = () => {
return (
<Stack gap="8">
<For each={['xs', 'sm', 'md']}>
{(size) => (
<TreeView.Root
key={size}
collection={collection}
maxW="sm"
size={size}
>
<TreeView.Label>Tree (size={size})</TreeView.Label>
<TreeView.Tree>
<TreeView.Node
indentGuide={<TreeView.BranchIndentGuide />}
render={({ node, nodeState }) =>
nodeState.isBranch ? (
<TreeView.BranchControl>
<LuFolder />
<TreeView.BranchText>{node.name}</TreeView.BranchText>
</TreeView.BranchControl>
) : (
<TreeView.Item>
<LuFile />
<TreeView.ItemText>{node.name}</TreeView.ItemText>
</TreeView.Item>
)
}
/>
</TreeView.Tree>
</TreeView.Root>
)}
</For>
</Stack>
)
}
interface Node {
id: string
name: string
children?: Node[]
}
const collection = createTreeCollection<Node>({
nodeToValue: (node) => node.id,
nodeToString: (node) => node.name,
rootNode: {
id: 'ROOT',
name: '',
children: [
{
id: 'node_modules',
name: 'node_modules',
children: [
{ id: 'node_modules/zag-js', name: 'zag-js' },
{ id: 'node_modules/pandacss', name: 'panda' },
{
id: 'node_modules/@types',
name: '@types',
children: [
{ id: 'node_modules/@types/react', name: 'react' },
{ id: 'node_modules/@types/react-dom', name: 'react-dom' },
],
},
],
},
{
id: 'src',
name: 'src',
children: [
{ id: 'src/app.tsx', name: 'app.tsx' },
{ id: 'src/index.ts', name: 'index.ts' },
],
},
{ id: 'panda.config', name: 'panda.config.ts' },
{ id: 'package.json', name: 'package.json' },
{ id: 'renovate.json', name: 'renovate.json' },
{ id: 'readme.md', name: 'README.md' },
],
},
})
Variants
Use the variant prop to change the variant of the tree view.
Tree (variant=subtle)
Tree (variant=solid)
'use client'
import { For, Stack, TreeView, createTreeCollection } from '@chakra-ui/react'
import { LuFile, LuFolder } from 'react-icons/lu'
export const TreeViewWithVariants = () => {
return (
<Stack gap="8">
<For each={['subtle', 'solid']}>
{(variant) => (
<TreeView.Root
key={variant}
collection={collection}
maxW="sm"
size="sm"
variant={variant}
colorPalette="teal"
defaultSelectedValue={['node_modules']}
>
<TreeView.Label>Tree (variant={variant})</TreeView.Label>
<TreeView.Tree>
<TreeView.Node
render={({ node, nodeState }) =>
nodeState.isBranch ? (
<TreeView.BranchControl>
<LuFolder />
<TreeView.BranchText>{node.name}</TreeView.BranchText>
</TreeView.BranchControl>
) : (
<TreeView.Item>
<LuFile />
<TreeView.ItemText>{node.name}</TreeView.ItemText>
</TreeView.Item>
)
}
/>
</TreeView.Tree>
</TreeView.Root>
)}
</For>
</Stack>
)
}
interface Node {
id: string
name: string
children?: Node[]
}
const collection = createTreeCollection<Node>({
nodeToValue: (node) => node.id,
nodeToString: (node) => node.name,
rootNode: {
id: 'ROOT',
name: '',
children: [
{
id: 'node_modules',
name: 'node_modules',
children: [
{ id: 'node_modules/zag-js', name: 'zag-js' },
{ id: 'node_modules/pandacss', name: 'panda' },
{
id: 'node_modules/@types',
name: '@types',
children: [
{ id: 'node_modules/@types/react', name: 'react' },
{ id: 'node_modules/@types/react-dom', name: 'react-dom' },
],
},
],
},
{
id: 'src',
name: 'src',
children: [
{ id: 'src/app.tsx', name: 'app.tsx' },
{ id: 'src/index.ts', name: 'index.ts' },
],
},
{ id: 'panda.config', name: 'panda.config.ts' },
{ id: 'package.json', name: 'package.json' },
{ id: 'renovate.json', name: 'renovate.json' },
{ id: 'readme.md', name: 'README.md' },
],
},
})
Colors
Use the colorPalette prop to change the color palette of the tree view.
Tree (colorPalette=gray)
Tree (colorPalette=zinc)
Tree (colorPalette=neutral)
Tree (colorPalette=stone)
Tree (colorPalette=red)
Tree (colorPalette=orange)
Tree (colorPalette=amber)
Tree (colorPalette=yellow)
Tree (colorPalette=lime)
Tree (colorPalette=green)
Tree (colorPalette=emerald)
Tree (colorPalette=teal)
Tree (colorPalette=cyan)
Tree (colorPalette=sky)
Tree (colorPalette=blue)
Tree (colorPalette=indigo)
Tree (colorPalette=violet)
Tree (colorPalette=purple)
Tree (colorPalette=fuchsia)
Tree (colorPalette=pink)
Tree (colorPalette=rose)
'use client'
import { For, TreeView, Wrap, createTreeCollection } from '@chakra-ui/react'
import { colorPalettes } from '#lib/color-palettes'
import { LuFile, LuFolder } from 'react-icons/lu'
export const TreeViewWithColors = () => {
return (
<Wrap gap="8">
<For each={colorPalettes}>
{(colorPalette) => (
<TreeView.Root
key={colorPalette}
collection={collection}
maxW="xs"
size="sm"
colorPalette={colorPalette}
defaultSelectedValue={['node_modules']}
>
<TreeView.Label>Tree (colorPalette={colorPalette})</TreeView.Label>
<TreeView.Tree>
<TreeView.Node
render={({ node, nodeState }) =>
nodeState.isBranch ? (
<TreeView.BranchControl>
<LuFolder />
<TreeView.BranchText>{node.name}</TreeView.BranchText>
</TreeView.BranchControl>
) : (
<TreeView.Item>
<LuFile />
<TreeView.ItemText>{node.name}</TreeView.ItemText>
</TreeView.Item>
)
}
/>
</TreeView.Tree>
</TreeView.Root>
)}
</For>
</Wrap>
)
}
interface Node {
id: string
name: string
children?: Node[]
}
const collection = createTreeCollection<Node>({
nodeToValue: (node) => node.id,
nodeToString: (node) => node.name,
rootNode: {
id: 'ROOT',
name: '',
children: [
{
id: 'node_modules',
name: 'node_modules',
children: [
{ id: 'node_modules/zag-js', name: 'zag-js' },
{ id: 'node_modules/pandacss', name: 'panda' },
{
id: 'node_modules/@types',
name: '@types',
children: [
{ id: 'node_modules/@types/react', name: 'react' },
{ id: 'node_modules/@types/react-dom', name: 'react-dom' },
],
},
],
},
{
id: 'src',
name: 'src',
children: [
{ id: 'src/app.tsx', name: 'app.tsx' },
{ id: 'src/index.ts', name: 'index.ts' },
],
},
{ id: 'panda.config', name: 'panda.config.ts' },
{ id: 'package.json', name: 'package.json' },
{ id: 'renovate.json', name: 'renovate.json' },
{ id: 'readme.md', name: 'README.md' },
],
},
})
Disabled Node
Adding the disabled prop to a node's property will disable the node and
prevent interaction.
Tree
'use client'
import { TreeView, createTreeCollection } from '@chakra-ui/react'
import { LuFile, LuFolder } from 'react-icons/lu'
export const TreeViewDisabledNode = () => {
return (
<TreeView.Root collection={collection} maxW="sm">
<TreeView.Label>Tree</TreeView.Label>
<TreeView.Tree>
<TreeView.Node
indentGuide={<TreeView.BranchIndentGuide />}
render={({ node, nodeState }) =>
nodeState.isBranch ? (
<TreeView.BranchControl>
<LuFolder />
<TreeView.BranchText>{node.name}</TreeView.BranchText>
</TreeView.BranchControl>
) : (
<TreeView.Item>
<LuFile />
<TreeView.ItemText>{node.name}</TreeView.ItemText>
</TreeView.Item>
)
}
/>
</TreeView.Tree>
</TreeView.Root>
)
}
interface Node {
id: string
name: string
disabled?: boolean
children?: Node[]
}
const collection = createTreeCollection<Node>({
nodeToValue: (node) => node.id,
nodeToString: (node) => node.name,
rootNode: {
id: 'ROOT',
name: '',
children: [
{
id: 'node_modules',
name: 'node_modules',
children: [
{ id: 'node_modules/zag-js', name: 'zag-js' },
{ id: 'node_modules/pandacss', name: 'panda' },
{
id: 'node_modules/@types',
name: '@types',
children: [
{ id: 'node_modules/@types/react', name: 'react' },
{ id: 'node_modules/@types/react-dom', name: 'react-dom' },
],
},
],
},
{
id: 'src',
name: 'src',
children: [
{ id: 'src/app.tsx', name: 'app.tsx' },
{ id: 'src/index.ts', name: 'index.ts' },
],
},
{ id: 'panda.config', name: 'panda.config.ts' },
{ id: 'package.json', name: 'package.json' },
{ id: 'renovate.json', name: 'renovate.json', disabled: true },
{ id: 'readme.md', name: 'README.md' },
],
},
})
Controlled Expansion
Use the expandedValue and onExpandedChange props to programmatically control
node expansion behavior.
Tree
'use client'
import { useState } from 'react'
import { TreeView, createTreeCollection } from '@chakra-ui/react'
import { LuFile, LuFolder } from 'react-icons/lu'
export const TreeViewControlledExpansion = () => {
const [expandedValue, setExpandedValue] = useState<string[]>(['node_modules'])
return (
<TreeView.Root
collection={collection}
maxW="sm"
expandedValue={expandedValue}
onExpandedChange={(e) => setExpandedValue(e.expandedValue)}
>
<TreeView.Label>Tree</TreeView.Label>
<TreeView.Tree>
<TreeView.Node<Node>
indentGuide={<TreeView.BranchIndentGuide />}
render={({ node }) =>
node.children ? (
<TreeView.BranchControl>
<LuFolder />
<TreeView.BranchText>{node.name}</TreeView.BranchText>
</TreeView.BranchControl>
) : (
<TreeView.Item>
<LuFile />
<TreeView.ItemText>{node.name}</TreeView.ItemText>
</TreeView.Item>
)
}
/>
</TreeView.Tree>
</TreeView.Root>
)
}
interface Node {
id: string
name: string
children?: Node[]
}
const collection = createTreeCollection<Node>({
nodeToValue: (node) => node.id,
nodeToString: (node) => node.name,
rootNode: {
id: 'ROOT',
name: '',
children: [
{
id: 'node_modules',
name: 'node_modules',
children: [
{ id: 'node_modules/zag-js', name: 'zag-js' },
{ id: 'node_modules/pandacss', name: 'panda' },
{
id: 'node_modules/@types',
name: '@types',
children: [
{ id: 'node_modules/@types/react', name: 'react' },
{ id: 'node_modules/@types/react-dom', name: 'react-dom' },
],
},
],
},
{
id: 'src',
name: 'src',
children: [
{ id: 'src/app.tsx', name: 'app.tsx' },
{ id: 'src/index.ts', name: 'index.ts' },
],
},
{ id: 'panda.config', name: 'panda.config.ts' },
{ id: 'package.json', name: 'package.json' },
{ id: 'renovate.json', name: 'renovate.json' },
{ id: 'readme.md', name: 'README.md' },
],
},
})
Expand Icon
Use the nodeState.expanded property to swap the rendered icon on the branch
when it's expanded or collapsed.
Tree
'use client'
import { TreeView, createTreeCollection } from '@chakra-ui/react'
import { LuSquareMinus, LuSquarePlus } from 'react-icons/lu'
export const TreeViewExpandIcon = () => {
return (
<TreeView.Root collection={collection} maxW="sm">
<TreeView.Label>Tree</TreeView.Label>
<TreeView.Tree>
<TreeView.Node
render={({ node, nodeState }) =>
nodeState.isBranch ? (
<TreeView.BranchControl>
{nodeState.expanded ? <LuSquareMinus /> : <LuSquarePlus />}
<TreeView.BranchText>{node.name}</TreeView.BranchText>
</TreeView.BranchControl>
) : (
<TreeView.Item>{node.name}</TreeView.Item>
)
}
/>
</TreeView.Tree>
</TreeView.Root>
)
}
interface Node {
id: string
name: string
children?: Node[]
}
const collection = createTreeCollection<Node>({
nodeToValue: (node) => node.id,
nodeToString: (node) => node.name,
rootNode: {
id: 'ROOT',
name: '',
children: [
{
id: 'node_modules',
name: 'node_modules',
children: [
{ id: 'node_modules/zag-js', name: 'zag-js' },
{ id: 'node_modules/pandacss', name: 'panda' },
{
id: 'node_modules/@types',
name: '@types',
children: [
{ id: 'node_modules/@types/react', name: 'react' },
{ id: 'node_modules/@types/react-dom', name: 'react-dom' },
],
},
],
},
{
id: 'src',
name: 'src',
children: [
{ id: 'src/app.tsx', name: 'app.tsx' },
{ id: 'src/index.ts', name: 'index.ts' },
],
},
{ id: 'panda.config', name: 'panda.config.ts' },
{ id: 'package.json', name: 'package.json' },
{ id: 'renovate.json', name: 'renovate.json' },
{ id: 'readme.md', name: 'README.md' },
],
},
})
Checkbox Tree
Add checkboxes to tree nodes for selection functionality. Render the
TreeView.NodeCheckbox component and read the node's checked state from the
useTreeViewNodeContext hook.
Tree
'use client'
import {
Checkmark,
TreeView,
createTreeCollection,
useTreeViewNodeContext,
} from '@chakra-ui/react'
import { LuFile, LuFolder } from 'react-icons/lu'
const TreeNodeCheckbox = (props: TreeView.NodeCheckboxProps) => {
const nodeState = useTreeViewNodeContext()
return (
<TreeView.NodeCheckbox aria-label="check node" {...props}>
<Checkmark
bg={{
base: 'bg',
_checked: 'colorPalette.solid',
_indeterminate: 'colorPalette.solid',
}}
size="sm"
checked={nodeState.checked === true}
indeterminate={nodeState.checked === 'indeterminate'}
/>
</TreeView.NodeCheckbox>
)
}
export const TreeViewCheckbox = () => {
return (
<TreeView.Root collection={collection} maxW="sm" defaultCheckedValue={[]}>
<TreeView.Label>Tree</TreeView.Label>
<TreeView.Tree>
<TreeView.Node
render={({ node, nodeState }) =>
nodeState.isBranch ? (
<TreeView.BranchControl role="none">
<TreeNodeCheckbox />
<LuFolder />
<TreeView.BranchText>{node.name}</TreeView.BranchText>
</TreeView.BranchControl>
) : (
<TreeView.Item>
<TreeNodeCheckbox />
<LuFile />
<TreeView.ItemText>{node.name}</TreeView.ItemText>
</TreeView.Item>
)
}
/>
</TreeView.Tree>
</TreeView.Root>
)
}
interface Node {
id: string
name: string
children?: Node[]
}
const collection = createTreeCollection<Node>({
nodeToValue: (node) => node.id,
nodeToString: (node) => node.name,
rootNode: {
id: 'ROOT',
name: '',
children: [
{
id: 'node_modules',
name: 'node_modules',
children: [
{ id: 'node_modules/zag-js', name: 'zag-js' },
{ id: 'node_modules/pandacss', name: 'panda' },
{
id: 'node_modules/@types',
name: '@types',
children: [
{ id: 'node_modules/@types/react', name: 'react' },
{ id: 'node_modules/@types/react-dom', name: 'react-dom' },
],
},
],
},
{
id: 'src',
name: 'src',
children: [
{ id: 'src/app.tsx', name: 'app.tsx' },
{ id: 'src/index.ts', name: 'index.ts' },
],
},
{ id: 'panda.config', name: 'panda.config.ts' },
{ id: 'package.json', name: 'package.json' },
{ id: 'renovate.json', name: 'renovate.json' },
{ id: 'readme.md', name: 'README.md' },
],
},
})
Props
Root
| Prop | Default | Type |
|---|---|---|
collection * | TreeCollection<T>The collection of tree nodes | |
expandOnClick | true | booleanWhether clicking on a branch should open it or not |
hideMode | ''display-none'' | HideModeHow to hide content when mounted but not present. - `'display-none'`: HTML `hidden` attribute. Effects stay alive. - `'activity'`: React 19 `<Activity mode="hidden">`. Effects pause. Requires React 19+. |
lazyMount | false | booleanWhether to enable lazy mounting |
selectionMode | '\'single\'' | 'multiple' | 'single'Whether the tree supports multiple selection - "single": only one node can be selected - "multiple": multiple nodes can be selected |
typeahead | true | booleanWhether the tree supports typeahead search |
unmountOnExit | false | booleanWhether to unmount on exit. |
colorPalette | 'gray' | 'base' | 'gray' | 'zinc' | 'neutral' | 'stone' | 'red' | 'orange' | 'amber' | 'yellow' | 'lime' | 'green' | 'emerald' | 'teal' | 'cyan' | 'sky' | 'blue' | 'indigo' | 'violet' | 'purple' | 'fuchsia' | 'pink' | 'rose' | 'sidebar' | 'sidebar.accent' | 'interaction' | 'accent' | 'presence' | 'status' | 'slate'The color palette of the component |
size | 'md' | 'md' | 'sm' | 'xs'The size of the component |
variant | 'subtle' | 'subtle' | 'solid'The variant of the component |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
canRename | (node: T, indexPath: IndexPath) => booleanFunction to determine if a node can be renamed | |
checkedValue | string[]The controlled checked node value | |
defaultCheckedValue | string[]The initial checked node value when rendered. Use when you don't need to control the checked node value. | |
defaultExpandedValue | string[]The initial expanded node ids when rendered. Use when you don't need to control the expanded node value. | |
defaultFocusedValue | stringThe initial focused node value when rendered. Use when you don't need to control the focused node value. | |
defaultSelectedValue | string[]The initial selected node value when rendered. Use when you don't need to control the selected node value. | |
expandedValue | string[]The controlled expanded node ids | |
focusedValue | stringThe value of the focused node | |
ids | Partial<{ root: string; tree: string; label: string; node: (value: string) => string }>The ids of the tree elements. Useful for composition. | |
loadChildren | (details: LoadChildrenDetails<T>) => Promise<T[]>Function to load children for a node asynchronously. When provided, branches will wait for this promise to resolve before expanding. | |
onBeforeRename | (details: RenameCompleteDetails) => booleanCalled before a rename is completed. Return false to prevent the rename. | |
onCheckedChange | (details: CheckedChangeDetails) => voidCalled when the checked value changes | |
onExpandedChange | (details: ExpandedChangeDetails<T>) => voidCalled when the tree is opened or closed | |
onFocusChange | (details: FocusChangeDetails<T>) => voidCalled when the focused node changes | |
onLoadChildrenComplete | (details: LoadChildrenCompleteDetails<T>) => voidCalled when a node finishes loading children | |
onLoadChildrenError | (details: LoadChildrenErrorDetails<T>) => voidCalled when loading children fails for one or more nodes | |
onRenameComplete | (details: RenameCompleteDetails) => voidCalled when a node label rename is completed | |
onRenameStart | (details: RenameStartDetails<T>) => voidCalled when a node starts being renamed | |
onSelectionChange | (details: SelectionChangeDetails<T>) => voidCalled when the selection changes | |
scrollToIndexFn | (details: ScrollToIndexDetails<T>) => voidFunction to scroll to a specific index. Useful for virtualized tree views. | |
selectedValue | string[]The controlled selected node value | |
translations | IntlTranslationsSpecifies the localized strings that identifies the accessibility elements and their states | |
animateContent | 'true' | 'false'The animateContent of the component |
Item
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |