Tree View
A component that is used to show a tree hierarchy.
Anatomy
To set up the tree view component correctly, you'll need to understand its anatomy and how we name its parts.
Each part includes a
data-partattribute to help identify them in the DOM.
Examples
Learn how to use the TreeView component in your project. Let's take a look at the most basic
example:
import { TreeView, createTreeCollection } from '@ark-ui/react/tree-view'
import { CheckSquareIcon, ChevronRightIcon, FileIcon, FolderIcon } from 'lucide-react'
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' },
    ],
  },
})
export const Basic = () => {
  return (
    <TreeView.Root collection={collection}>
      <TreeView.Label>Tree</TreeView.Label>
      <TreeView.Tree>
        {collection.rootNode.children?.map((node, index) => (
          <TreeNode key={node.id} node={node} indexPath={[index]} />
        ))}
      </TreeView.Tree>
    </TreeView.Root>
  )
}
const TreeNode = (props: TreeView.NodeProviderProps<Node>) => {
  const { node, indexPath } = props
  return (
    <TreeView.NodeProvider key={node.id} node={node} indexPath={indexPath}>
      {node.children ? (
        <TreeView.Branch>
          <TreeView.BranchControl>
            <TreeView.BranchText>
              <FolderIcon /> {node.name}
            </TreeView.BranchText>
            <TreeView.BranchIndicator>
              <ChevronRightIcon />
            </TreeView.BranchIndicator>
          </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.ItemIndicator>
            <CheckSquareIcon />
          </TreeView.ItemIndicator>
          <TreeView.ItemText>
            <FileIcon />
            {node.name}
          </TreeView.ItemText>
        </TreeView.Item>
      )}
    </TreeView.NodeProvider>
  )
}
import { TreeView, createTreeCollection } from '@ark-ui/solid/tree-view'
import { CheckSquareIcon, ChevronRightIcon, FileIcon, FolderIcon } from 'lucide-solid'
import { For, Show } from 'solid-js'
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' },
    ],
  },
})
export const Basic = () => {
  return (
    <TreeView.Root collection={collection}>
      <TreeView.Label>Tree</TreeView.Label>
      <TreeView.Tree>
        <For each={collection.rootNode.children}>
          {(node, index) => <TreeNode node={node} indexPath={[index()]} />}
        </For>
      </TreeView.Tree>
    </TreeView.Root>
  )
}
const TreeNode = (props: TreeView.NodeProviderProps<Node>) => {
  const { node, indexPath } = props
  return (
    <TreeView.NodeProvider node={node} indexPath={indexPath}>
      <Show
        when={node.children}
        fallback={
          <TreeView.Item>
            <TreeView.ItemIndicator>
              <CheckSquareIcon />
            </TreeView.ItemIndicator>
            <TreeView.ItemText>
              <FileIcon />
              {node.name}
            </TreeView.ItemText>
          </TreeView.Item>
        }
      >
        <TreeView.Branch>
          <TreeView.BranchControl>
            <TreeView.BranchText>
              <FolderIcon /> {node.name}
            </TreeView.BranchText>
            <TreeView.BranchIndicator>
              <ChevronRightIcon />
            </TreeView.BranchIndicator>
          </TreeView.BranchControl>
          <TreeView.BranchContent>
            <TreeView.BranchIndentGuide />
            <For each={node.children}>
              {(child, index) => <TreeNode node={child} indexPath={[...indexPath, index()]} />}
            </For>
          </TreeView.BranchContent>
        </TreeView.Branch>
      </Show>
    </TreeView.NodeProvider>
  )
}
<script setup lang="ts">
import { TreeView, createTreeCollection } from '@ark-ui/vue/tree-view'
import TreeNode from './tree-node.vue'
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' },
    ],
  },
})
</script>
<template>
  <TreeView.Root :collection="collection">
    <TreeView.Label>Tree</TreeView.Label>
    <TreeView.Tree>
      <TreeNode
        v-for="(node, index) in collection.rootNode.children"
        :key="node.id"
        :node="node"
        :indexPath="[index]"
      />
    </TreeView.Tree>
  </TreeView.Root>
</template>
Using the Root Provider
The RootProvider component provides a context for the tree-view. It accepts the value of the useTree-view hook.
You can leverage it to access the component state and methods from outside the tree-view.
import { TreeView, createTreeCollection, useTreeView } from '@ark-ui/react/tree-view'
import { CheckSquareIcon, ChevronRightIcon, FileIcon, FolderIcon } from 'lucide-react'
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' },
    ],
  },
})
export const RootProvider = () => {
  const treeView = useTreeView({ collection })
  return (
    <TreeView.RootProvider value={treeView}>
      <TreeView.Label>Tree</TreeView.Label>
      <TreeView.Tree>
        {collection.rootNode.children?.map((node, index) => (
          <TreeNode key={node.id} node={node} indexPath={[index]} />
        ))}
      </TreeView.Tree>
    </TreeView.RootProvider>
  )
}
const TreeNode = (props: TreeView.NodeProviderProps<Node>) => {
  const { node, indexPath } = props
  return (
    <TreeView.NodeProvider key={node.id} node={node} indexPath={indexPath}>
      {node.children ? (
        <TreeView.Branch>
          <TreeView.BranchControl>
            <TreeView.BranchText>
              <FolderIcon /> {node.name}
            </TreeView.BranchText>
            <TreeView.BranchIndicator>
              <ChevronRightIcon />
            </TreeView.BranchIndicator>
          </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.ItemIndicator>
            <CheckSquareIcon />
          </TreeView.ItemIndicator>
          <TreeView.ItemText>
            <FileIcon />
            {node.name}
          </TreeView.ItemText>
        </TreeView.Item>
      )}
    </TreeView.NodeProvider>
  )
}
import { TreeView, createTreeCollection, useTreeView } from '@ark-ui/solid/tree-view'
import { CheckSquareIcon, ChevronRightIcon, FileIcon, FolderIcon } from 'lucide-solid'
import { For, Show } from 'solid-js'
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' },
    ],
  },
})
export const RootProvider = () => {
  const treeView = useTreeView({ collection })
  return (
    <TreeView.RootProvider value={treeView}>
      <TreeView.Label>Tree</TreeView.Label>
      <TreeView.Tree>
        <For each={collection.rootNode.children}>
          {(node, index) => <TreeNode node={node} indexPath={[index()]} />}
        </For>
      </TreeView.Tree>
    </TreeView.RootProvider>
  )
}
const TreeNode = (props: TreeView.NodeProviderProps<Node>) => {
  const { node, indexPath } = props
  return (
    <TreeView.NodeProvider node={node} indexPath={indexPath}>
      <Show
        when={node.children}
        fallback={
          <TreeView.Item>
            <TreeView.ItemIndicator>
              <CheckSquareIcon />
            </TreeView.ItemIndicator>
            <TreeView.ItemText>
              <FileIcon />
              {node.name}
            </TreeView.ItemText>
          </TreeView.Item>
        }
      >
        <TreeView.Branch>
          <TreeView.BranchControl>
            <TreeView.BranchText>
              <FolderIcon /> {node.name}
            </TreeView.BranchText>
            <TreeView.BranchIndicator>
              <ChevronRightIcon />
            </TreeView.BranchIndicator>
          </TreeView.BranchControl>
          <TreeView.BranchContent>
            <TreeView.BranchIndentGuide />
            <For each={node.children}>
              {(child, index) => <TreeNode node={child} indexPath={[...indexPath, index()]} />}
            </For>
          </TreeView.BranchContent>
        </TreeView.Branch>
      </Show>
    </TreeView.NodeProvider>
  )
}
Example not foundIf you're using the
RootProvidercomponent, you don't need to use theRootcomponent.
API Reference
Root
| Prop | Default | Type | 
|---|---|---|
collection | TreeCollection<T>The collection of tree nodes  | |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
defaultExpandedValue | string[]The initial expanded items of the tree view. Use this when you do not need to control the state of the tree view.  | |
defaultSelectedValue | string[]The initial selected items of the tree view. Use this when you do not need to control the state of the tree view.  | |
expandedValue | string[]The id of the expanded nodes  | |
expandOnClick | true | booleanWhether clicking on a branch should open it or not  | 
focusedValue | stringThe id 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.  | |
lazyMount | false | booleanWhether to enable lazy mounting  | 
onExpandedChange | (details: ExpandedChangeDetails) => voidCalled when the tree is opened or closed  | |
onFocusChange | (details: FocusChangeDetails) => voidCalled when the focused node changes  | |
onSelectionChange | (details: SelectionChangeDetails) => voidCalled when the selection changes  | |
selectedValue | string[]The id of the selected nodes  | |
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.  | 
BranchContent
| 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. | 
| Data Attribute | Value | 
|---|---|
[data-scope] | tree-view | 
[data-part] | branch-content | 
[data-state] | "open" | "closed" | 
[data-depth] | The depth of the item | 
[data-path] | The path of the item | 
[data-value] | The value of the item | 
BranchControl
| 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. | 
| Data Attribute | Value | 
|---|---|
[data-scope] | tree-view | 
[data-part] | branch-control | 
[data-path] | The path of the item | 
[data-state] | "open" | "closed" | 
[data-disabled] | Present when disabled | 
[data-selected] | Present when selected | 
[data-focus] | Present when focused | 
[data-value] | The value of the item | 
[data-depth] | The depth of the item | 
BranchIndentGuide
| 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. | 
| Data Attribute | Value | 
|---|---|
[data-scope] | tree-view | 
[data-part] | branch-indent-guide | 
[data-depth] | The depth of the item | 
BranchIndicator
| 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. | 
| Data Attribute | Value | 
|---|---|
[data-scope] | tree-view | 
[data-part] | branch-indicator | 
[data-state] | "open" | "closed" | 
[data-disabled] | Present when disabled | 
[data-selected] | Present when selected | 
[data-focus] | Present when focused | 
Branch
| 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. | 
| Data Attribute | Value | 
|---|---|
[data-scope] | tree-view | 
[data-part] | branch | 
[data-depth] | The depth of the item | 
[data-branch] | |
[data-value] | The value of the item | 
[data-path] | The path of the item | 
[data-selected] | Present when selected | 
[data-state] | "open" | "closed" | 
[data-disabled] | Present when disabled | 
BranchText
| 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. | 
| Data Attribute | Value | 
|---|---|
[data-scope] | tree-view | 
[data-part] | branch-text | 
[data-disabled] | Present when disabled | 
[data-state] | "open" | "closed" | 
BranchTrigger
| 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. | 
| Data Attribute | Value | 
|---|---|
[data-scope] | tree-view | 
[data-part] | branch-trigger | 
[data-disabled] | Present when disabled | 
[data-state] | "open" | "closed" | 
[data-value] | The value of the item | 
ItemIndicator
| 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. | 
| Data Attribute | Value | 
|---|---|
[data-scope] | tree-view | 
[data-part] | item-indicator | 
[data-disabled] | Present when disabled | 
[data-selected] | Present when selected | 
[data-focus] | Present when focused | 
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. | 
| Data Attribute | Value | 
|---|---|
[data-scope] | tree-view | 
[data-part] | item | 
[data-path] | The path of the item | 
[data-value] | The value of the item | 
[data-focus] | Present when focused | 
[data-selected] | Present when selected | 
[data-disabled] | Present when disabled | 
[data-depth] | The depth of the item | 
ItemText
| 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. | 
| Data Attribute | Value | 
|---|---|
[data-scope] | tree-view | 
[data-part] | item-text | 
[data-disabled] | Present when disabled | 
[data-selected] | Present when selected | 
[data-focus] | Present when focused | 
Label
| 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. | 
NodeProvider
| Prop | Default | Type | 
|---|---|---|
indexPath | number[]The index path of the tree node  | |
node | NonNullable<T>The tree node  | 
RootProvider
| Prop | Default | Type | 
|---|---|---|
value | UseTreeViewReturn<T> | |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
lazyMount | false | booleanWhether to enable lazy mounting  | 
unmountOnExit | false | booleanWhether to unmount on exit.  | 
Tree
| 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. | 
Accessibility
Complies with the Tree View WAI-ARIA design pattern.
Keyboard Support
| Key | Description | 
|---|---|
Tab  | Moves focus to the tree view, placing the first tree view item in focus. | 
EnterSpace  | Selects the item or branch node | 
ArrowDown  | Moves focus to the next node | 
ArrowUp  | Moves focus to the previous node | 
ArrowRight  | When focus is on a closed branch node, opens the branch. When focus is on an open branch node, moves focus to the first item node.  | 
ArrowLeft  | When focus is on an open branch node, closes the node. When focus is on an item or branch node, moves focus to its parent branch node.  | 
Home  | Moves focus to first node without opening or closing a node. | 
End  | Moves focus to the last node that can be focused without expanding any nodes that are closed. | 
a-zA-Z  | Focus moves to the next node with a name that starts with the typed character. The search logic ignores nodes that are descendants of closed branch. | 
*  | Expands all sibling nodes that are at the same depth as the focused node. | 
Shift + ArrowDown  | Moves focus to and toggles the selection state of the next node. | 
Shift + ArrowUp  | Moves focus to and toggles the selection state of the previous node. | 
Ctrl + A  | Selects all nodes in the tree. If all nodes are selected, unselects all nodes. |