# Tree View

URL: https://ark-ui.com/docs/components/tree-view
Source: https://raw.githubusercontent.com/chakra-ui/ark/refs/heads/main/website/src/content/pages/components/tree-view.mdx

A component that is used to show a tree hierarchy.

---

<ComponentPreview id="TreeView" />

## Anatomy

<Anatomy id="tree-view" />

```tsx
<TreeView.Root>
  <TreeView.Label />
  <TreeView.Tree>
    <TreeView.NodeProvider>
      <TreeView.Branch>
        <TreeView.BranchControl>
          <TreeView.BranchIndicator />
          <TreeView.BranchText />
        </TreeView.BranchControl>
        <TreeView.BranchContent>
          <TreeView.BranchIndentGuide />
          <TreeView.Item>
            <TreeView.ItemText />
          </TreeView.Item>
        </TreeView.BranchContent>
      </TreeView.Branch>
    </TreeView.NodeProvider>
  </TreeView.Tree>
</TreeView.Root>
```

## Examples

**Example: basic**

#### React

```tsx
import { TreeView, createTreeCollection } from '@ark-ui/react/tree-view'
import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon } from 'lucide-react'
import styles from 'styles/tree-view.module.css'

export const Basic = () => {
  return (
    <TreeView.Root className={styles.Root} collection={collection}>
      <TreeView.Label className={styles.Label}>Tree</TreeView.Label>
      <TreeView.Tree className={styles.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}>
      <TreeView.NodeContext>
        {(nodeState) =>
          node.children ? (
            <TreeView.Branch className={styles.Branch}>
              <TreeView.BranchControl className={styles.BranchControl}>
                <TreeView.BranchIndicator className={styles.BranchIndicator}>
                  <ChevronRightIcon />
                </TreeView.BranchIndicator>
                <TreeView.BranchText className={styles.BranchText}>
                  {nodeState.expanded ? <FolderOpenIcon /> : <FolderIcon />}
                  {node.name}
                </TreeView.BranchText>
              </TreeView.BranchControl>
              <TreeView.BranchContent className={styles.BranchContent}>
                <TreeView.BranchIndentGuide className={styles.BranchIndentGuide} />
                {node.children.map((child, index) => (
                  <TreeNode key={child.id} node={child} indexPath={[...indexPath, index]} />
                ))}
              </TreeView.BranchContent>
            </TreeView.Branch>
          ) : (
            <TreeView.Item className={styles.Item}>
              <TreeView.ItemText className={styles.ItemText}>
                <FileIcon />
                {node.name}
              </TreeView.ItemText>
            </TreeView.Item>
          )
        }
      </TreeView.NodeContext>
    </TreeView.NodeProvider>
  )
}

interface Node {
  id: string
  name: string
  children?: Node[] | undefined
}

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' },
    ],
  },
})

```

#### Solid

```tsx
import { TreeView, createTreeCollection } from '@ark-ui/solid/tree-view'
import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon } from 'lucide-solid'
import { For } from 'solid-js'
import styles from 'styles/tree-view.module.css'

export const Basic = () => {
  return (
    <TreeView.Root class={styles.Root} collection={collection}>
      <TreeView.Label class={styles.Label}>Tree</TreeView.Label>
      <TreeView.Tree class={styles.Tree}>
        <For each={collection.rootNode.children}>{(node, index) => <TreeNode node={node} indexPath={[index()]} />}</For>
      </TreeView.Tree>
    </TreeView.Root>
  )
}

const TreeNode = (props: TreeView.NodeProviderProps<Node>) => {
  return (
    <TreeView.NodeProvider node={props.node} indexPath={props.indexPath}>
      <TreeView.NodeContext>
        {(nodeState) =>
          props.node.children ? (
            <TreeView.Branch class={styles.Branch}>
              <TreeView.BranchControl class={styles.BranchControl}>
                <TreeView.BranchIndicator class={styles.BranchIndicator}>
                  <ChevronRightIcon />
                </TreeView.BranchIndicator>
                <TreeView.BranchText class={styles.BranchText}>
                  {nodeState().expanded ? <FolderOpenIcon /> : <FolderIcon />}
                  {props.node.name}
                </TreeView.BranchText>
              </TreeView.BranchControl>
              <TreeView.BranchContent class={styles.BranchContent}>
                <TreeView.BranchIndentGuide class={styles.BranchIndentGuide} />
                <For each={props.node.children}>
                  {(child, index) => <TreeNode node={child} indexPath={[...props.indexPath, index()]} />}
                </For>
              </TreeView.BranchContent>
            </TreeView.Branch>
          ) : (
            <TreeView.Item class={styles.Item}>
              <TreeView.ItemText class={styles.ItemText}>
                <FileIcon />
                {props.node.name}
              </TreeView.ItemText>
            </TreeView.Item>
          )
        }
      </TreeView.NodeContext>
    </TreeView.NodeProvider>
  )
}

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' },
    ],
  },
})

```

#### Vue

```vue
<script setup lang="ts">
import { TreeView, createTreeCollection } from '@ark-ui/vue/tree-view'
import TreeNode from './tree-node.vue'
import styles from 'styles/tree-view.module.css'

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 :class="styles.Root" :collection="collection">
    <TreeView.Label :class="styles.Label">Tree</TreeView.Label>
    <TreeView.Tree :class="styles.Tree">
      <TreeNode
        v-for="(node, index) in collection.rootNode.children"
        :key="node.id"
        :node="node"
        :indexPath="[index]"
      />
    </TreeView.Tree>
  </TreeView.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { TreeView, createTreeCollection } from '$lib'
  import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon } from 'lucide-svelte'
  import styles from 'styles/tree-view.module.css'

  interface TreeNode {
    id: string
    name: string
    children?: TreeNode[]
  }

  const collection = createTreeCollection<TreeNode>({
    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>

<TreeView.Root class={styles.Root} {collection}>
  <TreeView.Label class={styles.Label}>Tree</TreeView.Label>
  <TreeView.Tree class={styles.Tree}>
    {#each collection.rootNode.children ?? [] as node, index (node.id)}
      {@render renderNode(node, [index])}
    {/each}
  </TreeView.Tree>
</TreeView.Root>

{#snippet renderNode(node: TreeNode, indexPath: number[])}
  <TreeView.NodeProvider {node} {indexPath}>
    <TreeView.NodeContext>
      {#snippet render(nodeState)}
        {#if node.children}
          <TreeView.Branch class={styles.Branch}>
            <TreeView.BranchControl class={styles.BranchControl}>
              <TreeView.BranchIndicator class={styles.BranchIndicator}>
                <ChevronRightIcon />
              </TreeView.BranchIndicator>
              <TreeView.BranchText class={styles.BranchText}>
                {#if nodeState().expanded}
                  <FolderOpenIcon />
                {:else}
                  <FolderIcon />
                {/if}
                {node.name}
              </TreeView.BranchText>
            </TreeView.BranchControl>
            <TreeView.BranchContent class={styles.BranchContent}>
              <TreeView.BranchIndentGuide class={styles.BranchIndentGuide} />
              {#each node.children as child, index (child.id)}
                {@render renderNode(child, [...indexPath, index])}
              {/each}
            </TreeView.BranchContent>
          </TreeView.Branch>
        {:else}
          <TreeView.Item class={styles.Item}>
            <TreeView.ItemText class={styles.ItemText}>
              <FileIcon />
              {node.name}
            </TreeView.ItemText>
          </TreeView.Item>
        {/if}
      {/snippet}
    </TreeView.NodeContext>
  </TreeView.NodeProvider>
{/snippet}

```

### Controlled Expanded

Pass the `expandedValue` and `onExpandedChange` props to the `TreeView.Root` component to control the expanded state of
the tree view.

**Example: controlled-expanded**

#### React

```tsx
import { TreeView, createTreeCollection } from '@ark-ui/react/tree-view'
import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon } from 'lucide-react'
import { useState } from 'react'
import styles from 'styles/tree-view.module.css'

export const ControlledExpanded = () => {
  const [expandedValue, setExpandedValue] = useState<string[]>(['node_modules'])
  return (
    <TreeView.Root
      className={styles.Root}
      collection={collection}
      expandedValue={expandedValue}
      onExpandedChange={({ expandedValue }) => setExpandedValue(expandedValue)}
    >
      <TreeView.Label className={styles.Label}>Tree</TreeView.Label>
      <TreeView.Tree className={styles.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}>
      <TreeView.NodeContext>
        {(nodeState) =>
          node.children ? (
            <TreeView.Branch className={styles.Branch}>
              <TreeView.BranchControl className={styles.BranchControl}>
                <TreeView.BranchIndicator className={styles.BranchIndicator}>
                  <ChevronRightIcon />
                </TreeView.BranchIndicator>
                <TreeView.BranchText className={styles.BranchText}>
                  {nodeState.expanded ? <FolderOpenIcon /> : <FolderIcon />}
                  {node.name}
                </TreeView.BranchText>
              </TreeView.BranchControl>
              <TreeView.BranchContent className={styles.BranchContent}>
                <TreeView.BranchIndentGuide className={styles.BranchIndentGuide} />
                {node.children.map((child, index) => (
                  <TreeNode key={child.id} node={child} indexPath={[...indexPath, index]} />
                ))}
              </TreeView.BranchContent>
            </TreeView.Branch>
          ) : (
            <TreeView.Item className={styles.Item}>
              <TreeView.ItemText className={styles.ItemText}>
                <FileIcon />
                {node.name}
              </TreeView.ItemText>
            </TreeView.Item>
          )
        }
      </TreeView.NodeContext>
    </TreeView.NodeProvider>
  )
}

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' },
    ],
  },
})

```

#### Solid

```tsx
import { TreeView, createTreeCollection } from '@ark-ui/solid/tree-view'
import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon } from 'lucide-solid'
import { For, createSignal } from 'solid-js'
import styles from 'styles/tree-view.module.css'

export const ControlledExpanded = () => {
  const [expandedValue, setExpandedValue] = createSignal<string[]>(['node_modules'])

  return (
    <TreeView.Root
      class={styles.Root}
      collection={collection}
      expandedValue={expandedValue()}
      onExpandedChange={({ expandedValue }) => setExpandedValue(expandedValue)}
    >
      <TreeView.Label class={styles.Label}>Tree</TreeView.Label>
      <TreeView.Tree class={styles.Tree}>
        <For each={collection.rootNode.children}>{(node, index) => <TreeNode node={node} indexPath={[index()]} />}</For>
      </TreeView.Tree>
    </TreeView.Root>
  )
}

const TreeNode = (props: TreeView.NodeProviderProps<Node>) => {
  return (
    <TreeView.NodeProvider node={props.node} indexPath={props.indexPath}>
      <TreeView.NodeContext>
        {(nodeState) =>
          props.node.children ? (
            <TreeView.Branch class={styles.Branch}>
              <TreeView.BranchControl class={styles.BranchControl}>
                <TreeView.BranchIndicator class={styles.BranchIndicator}>
                  <ChevronRightIcon />
                </TreeView.BranchIndicator>
                <TreeView.BranchText class={styles.BranchText}>
                  {nodeState().expanded ? <FolderOpenIcon /> : <FolderIcon />}
                  {props.node.name}
                </TreeView.BranchText>
              </TreeView.BranchControl>
              <TreeView.BranchContent class={styles.BranchContent}>
                <TreeView.BranchIndentGuide class={styles.BranchIndentGuide} />
                <For each={props.node.children}>
                  {(child, index) => <TreeNode node={child} indexPath={[...props.indexPath, index()]} />}
                </For>
              </TreeView.BranchContent>
            </TreeView.Branch>
          ) : (
            <TreeView.Item class={styles.Item}>
              <TreeView.ItemText class={styles.ItemText}>
                <FileIcon />
                {props.node.name}
              </TreeView.ItemText>
            </TreeView.Item>
          )
        }
      </TreeView.NodeContext>
    </TreeView.NodeProvider>
  )
}

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' },
    ],
  },
})

```

#### Vue

```vue
<script setup lang="ts">
import { TreeView, createTreeCollection } from '@ark-ui/vue/tree-view'
import { ref } from 'vue'
import TreeNode from './tree-node.vue'
import styles from 'styles/tree-view.module.css'

interface Node {
  id: string
  name: string
  children?: Node[]
}

const expandedValue = ref<string[]>(['node_modules'])

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 :class="styles.Root" :collection="collection" v-model:expanded-value="expandedValue">
    <TreeView.Label :class="styles.Label">Tree</TreeView.Label>
    <TreeView.Tree :class="styles.Tree">
      <TreeNode
        v-for="(node, index) in collection.rootNode.children"
        :key="node.id"
        :node="node"
        :indexPath="[index]"
      />
    </TreeView.Tree>
  </TreeView.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { TreeView, createTreeCollection } from '$lib'
  import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon } from 'lucide-svelte'
  import styles from 'styles/tree-view.module.css'

  interface TreeNode {
    id: string
    name: string
    children?: TreeNode[]
  }

  const collection = createTreeCollection<TreeNode>({
    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: 'src',
          name: 'src',
          children: [
            { id: 'src/app.tsx', name: 'app.tsx' },
            { id: 'src/index.ts', name: 'index.ts' },
          ],
        },
        { id: 'package.json', name: 'package.json' },
      ],
    },
  })

  let expandedValue = $state(['node_modules'])
</script>

<TreeView.Root class={styles.Root} {collection} bind:expandedValue>
  <TreeView.Label class={styles.Label}>Tree</TreeView.Label>
  <TreeView.Tree class={styles.Tree}>
    {#each collection.rootNode.children ?? [] as node, index (node.id)}
      {@render renderNode(node, [index])}
    {/each}
  </TreeView.Tree>
</TreeView.Root>

{#snippet renderNode(node: TreeNode, indexPath: number[])}
  <TreeView.NodeProvider {node} {indexPath}>
    <TreeView.NodeContext>
      {#snippet render(nodeState)}
        {#if node.children}
          <TreeView.Branch class={styles.Branch}>
            <TreeView.BranchControl class={styles.BranchControl}>
              <TreeView.BranchIndicator class={styles.BranchIndicator}>
                <ChevronRightIcon />
              </TreeView.BranchIndicator>
              <TreeView.BranchText class={styles.BranchText}>
                {#if nodeState().expanded}
                  <FolderOpenIcon />
                {:else}
                  <FolderIcon />
                {/if}
                {node.name}
              </TreeView.BranchText>
            </TreeView.BranchControl>
            <TreeView.BranchContent class={styles.BranchContent}>
              <TreeView.BranchIndentGuide class={styles.BranchIndentGuide} />
              {#each node.children as child, index (child.id)}
                {@render renderNode(child, [...indexPath, index])}
              {/each}
            </TreeView.BranchContent>
          </TreeView.Branch>
        {:else}
          <TreeView.Item class={styles.Item}>
            <TreeView.ItemText class={styles.ItemText}>
              <FileIcon />
              {node.name}
            </TreeView.ItemText>
          </TreeView.Item>
        {/if}
      {/snippet}
    </TreeView.NodeContext>
  </TreeView.NodeProvider>
{/snippet}

```

### Controlled Selection

Pass the `selectedValue` and `onSelectionChange` props to the `TreeView.Root` component to control the selected state of
the tree view.

**Example: controlled-selected**

#### React

```tsx
import { TreeView, createTreeCollection } from '@ark-ui/react/tree-view'
import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon } from 'lucide-react'
import { useState } from 'react'
import styles from 'styles/tree-view.module.css'

export const ControlledSelected = () => {
  const [selectedValue, setSelectedValue] = useState<string[]>(['package.json'])
  return (
    <TreeView.Root
      className={styles.Root}
      collection={collection}
      selectedValue={selectedValue}
      onSelectionChange={({ selectedValue }) => setSelectedValue(selectedValue)}
    >
      <TreeView.Label className={styles.Label}>Tree</TreeView.Label>
      <TreeView.Tree className={styles.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}>
      <TreeView.NodeContext>
        {(nodeState) =>
          node.children ? (
            <TreeView.Branch className={styles.Branch}>
              <TreeView.BranchControl className={styles.BranchControl}>
                <TreeView.BranchIndicator className={styles.BranchIndicator}>
                  <ChevronRightIcon />
                </TreeView.BranchIndicator>
                <TreeView.BranchText className={styles.BranchText}>
                  {nodeState.expanded ? <FolderOpenIcon /> : <FolderIcon />}
                  {node.name}
                </TreeView.BranchText>
              </TreeView.BranchControl>
              <TreeView.BranchContent className={styles.BranchContent}>
                <TreeView.BranchIndentGuide className={styles.BranchIndentGuide} />
                {node.children.map((child, index) => (
                  <TreeNode key={child.id} node={child} indexPath={[...indexPath, index]} />
                ))}
              </TreeView.BranchContent>
            </TreeView.Branch>
          ) : (
            <TreeView.Item className={styles.Item}>
              <TreeView.ItemText className={styles.ItemText}>
                <FileIcon />
                {node.name}
              </TreeView.ItemText>
            </TreeView.Item>
          )
        }
      </TreeView.NodeContext>
    </TreeView.NodeProvider>
  )
}

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' },
    ],
  },
})

```

#### Solid

```tsx
import { TreeView, createTreeCollection } from '@ark-ui/solid/tree-view'
import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon } from 'lucide-solid'
import { For, createSignal } from 'solid-js'
import styles from 'styles/tree-view.module.css'

export const ControlledSelected = () => {
  const [selectedValue, setSelectedValue] = createSignal<string[]>(['package.json'])

  return (
    <TreeView.Root
      class={styles.Root}
      collection={collection}
      selectedValue={selectedValue()}
      onSelectionChange={({ selectedValue }) => setSelectedValue(selectedValue)}
    >
      <TreeView.Label class={styles.Label}>Tree</TreeView.Label>
      <TreeView.Tree class={styles.Tree}>
        <For each={collection.rootNode.children}>{(node, index) => <TreeNode node={node} indexPath={[index()]} />}</For>
      </TreeView.Tree>
    </TreeView.Root>
  )
}

const TreeNode = (props: TreeView.NodeProviderProps<Node>) => {
  return (
    <TreeView.NodeProvider node={props.node} indexPath={props.indexPath}>
      <TreeView.NodeContext>
        {(nodeState) =>
          props.node.children ? (
            <TreeView.Branch class={styles.Branch}>
              <TreeView.BranchControl class={styles.BranchControl}>
                <TreeView.BranchIndicator class={styles.BranchIndicator}>
                  <ChevronRightIcon />
                </TreeView.BranchIndicator>
                <TreeView.BranchText class={styles.BranchText}>
                  {nodeState().expanded ? <FolderOpenIcon /> : <FolderIcon />}
                  {props.node.name}
                </TreeView.BranchText>
              </TreeView.BranchControl>
              <TreeView.BranchContent class={styles.BranchContent}>
                <TreeView.BranchIndentGuide class={styles.BranchIndentGuide} />
                <For each={props.node.children}>
                  {(child, index) => <TreeNode node={child} indexPath={[...props.indexPath, index()]} />}
                </For>
              </TreeView.BranchContent>
            </TreeView.Branch>
          ) : (
            <TreeView.Item class={styles.Item}>
              <TreeView.ItemText class={styles.ItemText}>
                <FileIcon />
                {props.node.name}
              </TreeView.ItemText>
            </TreeView.Item>
          )
        }
      </TreeView.NodeContext>
    </TreeView.NodeProvider>
  )
}

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' },
    ],
  },
})

```

#### Vue

```vue
<script setup lang="ts">
import { TreeView, createTreeCollection } from '@ark-ui/vue/tree-view'
import { ref } from 'vue'
import TreeNode from './tree-node.vue'
import styles from 'styles/tree-view.module.css'

interface Node {
  id: string
  name: string
  children?: Node[]
}

const selectedValue = ref<string[]>(['package.json'])

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 :class="styles.Root" :collection="collection" v-model:selected-value="selectedValue">
    <TreeView.Label :class="styles.Label">Tree</TreeView.Label>
    <TreeView.Tree :class="styles.Tree">
      <TreeNode
        v-for="(node, index) in collection.rootNode.children"
        :key="node.id"
        :node="node"
        :indexPath="[index]"
      />
    </TreeView.Tree>
  </TreeView.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { TreeView, createTreeCollection } from '$lib'
  import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon } from 'lucide-svelte'
  import styles from 'styles/tree-view.module.css'

  interface TreeNode {
    id: string
    name: string
    children?: TreeNode[]
  }

  const collection = createTreeCollection<TreeNode>({
    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: 'src',
          name: 'src',
          children: [
            { id: 'src/app.tsx', name: 'app.tsx' },
            { id: 'src/index.ts', name: 'index.ts' },
          ],
        },
        { id: 'package.json', name: 'package.json' },
      ],
    },
  })

  let selectedValue = $state(['package.json'])
</script>

<pre>Selected: {JSON.stringify(selectedValue)}</pre>

<TreeView.Root class={styles.Root} {collection} bind:selectedValue>
  <TreeView.Label class={styles.Label}>Tree</TreeView.Label>
  <TreeView.Tree class={styles.Tree}>
    {#each collection.rootNode.children ?? [] as node, index (node.id)}
      {@render renderNode(node, [index])}
    {/each}
  </TreeView.Tree>
</TreeView.Root>

{#snippet renderNode(node: TreeNode, indexPath: number[])}
  <TreeView.NodeProvider {node} {indexPath}>
    <TreeView.NodeContext>
      {#snippet render(nodeState)}
        {#if node.children}
          <TreeView.Branch class={styles.Branch}>
            <TreeView.BranchControl class={styles.BranchControl}>
              <TreeView.BranchIndicator class={styles.BranchIndicator}>
                <ChevronRightIcon />
              </TreeView.BranchIndicator>
              <TreeView.BranchText class={styles.BranchText}>
                {#if nodeState().expanded}
                  <FolderOpenIcon />
                {:else}
                  <FolderIcon />
                {/if}
                {node.name}
              </TreeView.BranchText>
            </TreeView.BranchControl>
            <TreeView.BranchContent class={styles.BranchContent}>
              <TreeView.BranchIndentGuide class={styles.BranchIndentGuide} />
              {#each node.children as child, index (child.id)}
                {@render renderNode(child, [...indexPath, index])}
              {/each}
            </TreeView.BranchContent>
          </TreeView.Branch>
        {:else}
          <TreeView.Item class={styles.Item}>
            <TreeView.ItemText class={styles.ItemText}>
              <FileIcon />
              {node.name}
            </TreeView.ItemText>
          </TreeView.Item>
        {/if}
      {/snippet}
    </TreeView.NodeContext>
  </TreeView.NodeProvider>
{/snippet}

```

### Root Provider

An alternative way to control the tree view is to use the `RootProvider` component and the `useTreeView` hook. This way
you can access the state and methods from outside the component.

**Example: root-provider**

#### React

```tsx
import { TreeView, createTreeCollection, useTreeView } from '@ark-ui/react/tree-view'
import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon } from 'lucide-react'
import styles from 'styles/tree-view.module.css'

export const RootProvider = () => {
  const treeView = useTreeView({ collection })

  return (
    <div className="stack">
      <output>selected: {JSON.stringify(treeView.selectedValue)}</output>
      <TreeView.RootProvider className={styles.Root} value={treeView}>
        <TreeView.Label className={styles.Label}>Tree</TreeView.Label>
        <TreeView.Tree className={styles.Tree}>
          {collection.rootNode.children?.map((node, index) => (
            <TreeNode key={node.id} node={node} indexPath={[index]} />
          ))}
        </TreeView.Tree>
      </TreeView.RootProvider>
    </div>
  )
}

const TreeNode = (props: TreeView.NodeProviderProps<Node>) => {
  const { node, indexPath } = props
  return (
    <TreeView.NodeProvider key={node.id} node={node} indexPath={indexPath}>
      <TreeView.NodeContext>
        {(nodeState) =>
          node.children ? (
            <TreeView.Branch className={styles.Branch}>
              <TreeView.BranchControl className={styles.BranchControl}>
                <TreeView.BranchIndicator className={styles.BranchIndicator}>
                  <ChevronRightIcon />
                </TreeView.BranchIndicator>
                <TreeView.BranchText className={styles.BranchText}>
                  {nodeState.expanded ? <FolderOpenIcon /> : <FolderIcon />}
                  {node.name}
                </TreeView.BranchText>
              </TreeView.BranchControl>
              <TreeView.BranchContent className={styles.BranchContent}>
                <TreeView.BranchIndentGuide className={styles.BranchIndentGuide} />
                {node.children.map((child, index) => (
                  <TreeNode key={child.id} node={child} indexPath={[...indexPath, index]} />
                ))}
              </TreeView.BranchContent>
            </TreeView.Branch>
          ) : (
            <TreeView.Item className={styles.Item}>
              <TreeView.ItemText className={styles.ItemText}>
                <FileIcon />
                {node.name}
              </TreeView.ItemText>
            </TreeView.Item>
          )
        }
      </TreeView.NodeContext>
    </TreeView.NodeProvider>
  )
}

interface Node {
  id: string
  name: string
  children?: Node[] | undefined
}

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' },
    ],
  },
})

```

#### Solid

```tsx
import { TreeView, createTreeCollection, useTreeView } from '@ark-ui/solid/tree-view'
import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon } from 'lucide-solid'
import { For } from 'solid-js'
import styles from 'styles/tree-view.module.css'

export const RootProvider = () => {
  const treeView = useTreeView({ collection })

  return (
    <TreeView.RootProvider class={styles.Root} value={treeView}>
      <TreeView.Label class={styles.Label}>Tree</TreeView.Label>
      <TreeView.Tree class={styles.Tree}>
        <For each={collection.rootNode.children}>{(node, index) => <TreeNode node={node} indexPath={[index()]} />}</For>
      </TreeView.Tree>
    </TreeView.RootProvider>
  )
}

const TreeNode = (props: TreeView.NodeProviderProps<Node>) => {
  return (
    <TreeView.NodeProvider node={props.node} indexPath={props.indexPath}>
      <TreeView.NodeContext>
        {(nodeState) =>
          props.node.children ? (
            <TreeView.Branch class={styles.Branch}>
              <TreeView.BranchControl class={styles.BranchControl}>
                <TreeView.BranchIndicator class={styles.BranchIndicator}>
                  <ChevronRightIcon />
                </TreeView.BranchIndicator>
                <TreeView.BranchText class={styles.BranchText}>
                  {nodeState().expanded ? <FolderOpenIcon /> : <FolderIcon />}
                  {props.node.name}
                </TreeView.BranchText>
              </TreeView.BranchControl>
              <TreeView.BranchContent class={styles.BranchContent}>
                <TreeView.BranchIndentGuide class={styles.BranchIndentGuide} />
                <For each={props.node.children}>
                  {(child, index) => <TreeNode node={child} indexPath={[...props.indexPath, index()]} />}
                </For>
              </TreeView.BranchContent>
            </TreeView.Branch>
          ) : (
            <TreeView.Item class={styles.Item}>
              <TreeView.ItemText class={styles.ItemText}>
                <FileIcon />
                {props.node.name}
              </TreeView.ItemText>
            </TreeView.Item>
          )
        }
      </TreeView.NodeContext>
    </TreeView.NodeProvider>
  )
}

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' },
    ],
  },
})

```

#### Vue

```vue
<script setup lang="ts">
import { TreeView, createTreeCollection, useTreeView } from '@ark-ui/vue/tree-view'
import TreeNode from './tree-node.vue'
import styles from 'styles/tree-view.module.css'

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' },
    ],
  },
})

const treeView = useTreeView({
  collection,
})
</script>

<template>
  <TreeView.RootProvider :class="styles.Root" :value="treeView">
    <TreeView.Label :class="styles.Label">Tree</TreeView.Label>
    <TreeView.Tree :class="styles.Tree">
      <TreeNode
        v-for="(node, index) in collection.rootNode.children"
        :key="node.id"
        :node="node"
        :indexPath="[index]"
      />
    </TreeView.Tree>
  </TreeView.RootProvider>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { TreeView, createTreeCollection, useTreeView } from '$lib'
  import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon } from 'lucide-svelte'
  import styles from 'styles/tree-view.module.css'

  interface TreeNode {
    id: string
    name: string
    children?: TreeNode[]
  }

  const collection = createTreeCollection<TreeNode>({
    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: 'src',
          name: 'src',
          children: [
            { id: 'src/app.tsx', name: 'app.tsx' },
            { id: 'src/index.ts', name: 'index.ts' },
          ],
        },
        { id: 'package.json', name: 'package.json' },
      ],
    },
  })

  const id = $props.id()
  const treeView = useTreeView({ collection, id })
</script>

<TreeView.RootProvider class={styles.Root} value={treeView}>
  <TreeView.Label class={styles.Label}>Root Provider Tree</TreeView.Label>
  <TreeView.Tree class={styles.Tree}>
    {#each collection.rootNode.children ?? [] as node, index (node.id)}
      {@render renderNode(node, [index])}
    {/each}
  </TreeView.Tree>
</TreeView.RootProvider>

{#snippet renderNode(node: TreeNode, indexPath: number[])}
  <TreeView.NodeProvider {node} {indexPath}>
    <TreeView.NodeContext>
      {#snippet render(nodeState)}
        {#if node.children}
          <TreeView.Branch class={styles.Branch}>
            <TreeView.BranchControl class={styles.BranchControl}>
              <TreeView.BranchIndicator class={styles.BranchIndicator}>
                <ChevronRightIcon />
              </TreeView.BranchIndicator>
              <TreeView.BranchText class={styles.BranchText}>
                {#if nodeState().expanded}
                  <FolderOpenIcon />
                {:else}
                  <FolderIcon />
                {/if}
                {node.name}
              </TreeView.BranchText>
            </TreeView.BranchControl>
            <TreeView.BranchContent class={styles.BranchContent}>
              <TreeView.BranchIndentGuide class={styles.BranchIndentGuide} />
              {#each node.children as child, index (child.id)}
                {@render renderNode(child, [...indexPath, index])}
              {/each}
            </TreeView.BranchContent>
          </TreeView.Branch>
        {:else}
          <TreeView.Item class={styles.Item}>
            <TreeView.ItemText class={styles.ItemText}>
              <FileIcon />
              {node.name}
            </TreeView.ItemText>
          </TreeView.Item>
        {/if}
      {/snippet}
    </TreeView.NodeContext>
  </TreeView.NodeProvider>
{/snippet}

```

### Lazy Loading

Lazy loading is a feature that allows the tree view to load children of a node on demand (or async). This helps to
improve the initial load time and memory usage.

To use this, you need to provide the following:

- `loadChildren` — A function that is used to load the children of a node.
- `onLoadChildrenComplete` — A callback that is called when the children of a node are loaded. Used to update the tree
  collection.
- `childrenCount` — A number that indicates the number of children of a branch node.

**Example: async-loading**

#### React

```tsx
import { TreeView, createTreeCollection, useTreeViewNodeContext } from '@ark-ui/react/tree-view'
import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon, LoaderIcon } from 'lucide-react'
import { useState } from 'react'
import styles from 'styles/tree-view.module.css'

// mock api result
const response: Record<string, Node[]> = {
  node_modules: [
    { id: 'zag-js', name: 'zag-js' },
    { id: 'pandacss', name: 'panda' },
    { id: '@types', name: '@types', childrenCount: 2 },
  ],
  'node_modules/@types': [
    { id: 'react', name: 'react' },
    { id: 'react-dom', name: 'react-dom' },
  ],
  src: [
    { id: 'app.tsx', name: 'app.tsx' },
    { id: 'index.ts', name: 'index.ts' },
  ],
}

// function to load children of a node
function loadChildren(details: TreeView.LoadChildrenDetails<Node>): Promise<Node[]> {
  const value = details.valuePath.join('/')
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(response[value] ?? [])
    }, 500)
  })
}

export const AsyncLoading = () => {
  const [collection, setCollection] = useState(initialCollection)
  return (
    <TreeView.Root
      className={styles.Root}
      collection={collection}
      loadChildren={loadChildren}
      onLoadChildrenComplete={(e) => setCollection(e.collection)}
    >
      <TreeView.Label className={styles.Label}>Tree</TreeView.Label>
      <TreeView.Tree className={styles.Tree}>
        {collection.rootNode.children?.map((node, index) => (
          <TreeNode key={node.id} node={node} indexPath={[index]} />
        ))}
      </TreeView.Tree>
    </TreeView.Root>
  )
}

function TreeBranchIcon() {
  const nodeState = useTreeViewNodeContext()
  if (nodeState.loading) return <LoaderIcon className={styles.Loader} />
  return nodeState.expanded ? <FolderOpenIcon /> : <FolderIcon />
}

const TreeNode = (props: TreeView.NodeProviderProps<Node>) => {
  const { node, indexPath } = props
  return (
    <TreeView.NodeProvider key={node.id} node={node} indexPath={indexPath}>
      {node.children || node.childrenCount ? (
        <TreeView.Branch className={styles.Branch}>
          <TreeView.BranchControl className={styles.BranchControl}>
            <TreeView.BranchIndicator className={styles.BranchIndicator}>
              <ChevronRightIcon />
            </TreeView.BranchIndicator>
            <TreeView.BranchText className={styles.BranchText}>
              <TreeBranchIcon /> {node.name}
            </TreeView.BranchText>
          </TreeView.BranchControl>
          <TreeView.BranchContent className={styles.BranchContent}>
            <TreeView.BranchIndentGuide className={styles.BranchIndentGuide} />
            {node.children?.map((child, index) => (
              <TreeNode key={child.id} node={child} indexPath={[...indexPath, index]} />
            ))}
          </TreeView.BranchContent>
        </TreeView.Branch>
      ) : (
        <TreeView.Item className={styles.Item}>
          <TreeView.ItemText className={styles.ItemText}>
            <FileIcon />
            {node.name}
          </TreeView.ItemText>
        </TreeView.Item>
      )}
    </TreeView.NodeProvider>
  )
}

interface Node {
  id: string
  name: string
  children?: Node[]
  childrenCount?: number
}

const initialCollection = createTreeCollection<Node>({
  nodeToValue: (node) => node.id,
  nodeToString: (node) => node.name,
  rootNode: {
    id: 'ROOT',
    name: '',
    children: [
      { id: 'node_modules', name: 'node_modules', childrenCount: 3 },
      { id: 'src', name: 'src', childrenCount: 2 },
      { 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' },
    ],
  },
})

```

#### Solid

```tsx
import { TreeView, createTreeCollection } from '@ark-ui/solid/tree-view'
import { SquareCheckBigIcon, ChevronRightIcon, FileIcon, FolderIcon, LoaderCircleIcon } from 'lucide-solid'
import { For, createSignal } from 'solid-js'
import { useTreeViewNodeContext } from '../use-tree-view-node-context'

// mock api result
const response: Record<string, Node[]> = {
  node_modules: [
    { id: 'zag-js', name: 'zag-js' },
    { id: 'pandacss', name: 'panda' },
    { id: '@types', name: '@types', childrenCount: 2 },
  ],
  'node_modules/@types': [
    { id: 'react', name: 'react' },
    { id: 'react-dom', name: 'react-dom' },
  ],
  src: [
    { id: 'app.tsx', name: 'app.tsx' },
    { id: 'index.ts', name: 'index.ts' },
  ],
}

// function to load children of a node
function loadChildren(details: TreeView.LoadChildrenDetails<Node>): Promise<Node[]> {
  const value = details.valuePath.join('/')
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(response[value] ?? [])
    }, 1200)
  })
}

export const AsyncLoading = () => {
  const [collection, setCollection] = createSignal(initialCollection)
  return (
    <TreeView.Root
      collection={collection()}
      loadChildren={loadChildren}
      onLoadChildrenComplete={(e) => setCollection(e.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>
  )
}

function TreeNodeIndicator() {
  const nodeState = useTreeViewNodeContext()
  return nodeState().loading ? <LoaderCircleIcon style={{ animation: 'spin 1s infinite' }} /> : <FolderIcon />
}

const TreeNode = (props: TreeView.NodeProviderProps<Node>) => {
  const { node, indexPath } = props
  return (
    <TreeView.NodeProvider node={node} indexPath={indexPath}>
      {node.children || node.childrenCount ? (
        <TreeView.Branch>
          <TreeView.BranchControl>
            <TreeView.BranchText>
              <TreeNodeIndicator /> {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>
      ) : (
        <TreeView.Item>
          <TreeView.ItemIndicator>
            <SquareCheckBigIcon />
          </TreeView.ItemIndicator>
          <TreeView.ItemText>
            <FileIcon />
            {node.name}
          </TreeView.ItemText>
        </TreeView.Item>
      )}
    </TreeView.NodeProvider>
  )
}

interface Node {
  id: string
  name: string
  children?: Node[]
  childrenCount?: number
}

const initialCollection = createTreeCollection<Node>({
  nodeToValue: (node) => node.id,
  nodeToString: (node) => node.name,
  rootNode: {
    id: 'ROOT',
    name: '',
    children: [
      { id: 'node_modules', name: 'node_modules', childrenCount: 3 },
      { id: 'src', name: 'src', childrenCount: 2 },
      { 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' },
    ],
  },
})

```

#### Vue

```vue
<script setup lang="ts">
import {
  type TreeCollection,
  TreeView,
  type TreeView as TreeViewTypes,
  createTreeCollection,
} from '@ark-ui/vue/tree-view'
import { type Ref, ref } from 'vue'
import AsyncTreeNode from './async-tree-node.vue'

interface Node {
  id: string
  name: string
  children?: Node[]
  childrenCount?: number
}

// mock api result
const response: Record<string, Node[]> = {
  node_modules: [
    { id: 'zag-js', name: 'zag-js' },
    { id: 'pandacss', name: 'panda' },
    { id: '@types', name: '@types', childrenCount: 2 },
  ],
  'node_modules/@types': [
    { id: 'react', name: 'react' },
    { id: 'react-dom', name: 'react-dom' },
  ],
  src: [
    { id: 'app.tsx', name: 'app.tsx' },
    { id: 'index.ts', name: 'index.ts' },
  ],
}

// function to load children of a node
function loadChildren(details: TreeViewTypes.LoadChildrenDetails<Node>): Promise<Node[]> {
  const value = details.valuePath.join('/')
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(response[value] ?? [])
    }, 1200)
  })
}

const initialCollection = createTreeCollection<Node>({
  nodeToValue: (node) => node.id,
  nodeToString: (node) => node.name,
  rootNode: {
    id: 'ROOT',
    name: '',
    children: [
      { id: 'node_modules', name: 'node_modules', childrenCount: 3 },
      { id: 'src', name: 'src', childrenCount: 2 },
      { 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' },
    ],
  },
})

const collection = ref(initialCollection) as Ref<TreeCollection<Node>>
</script>

<template>
  <TreeView.Root
    :collection="collection"
    :loadChildren="loadChildren"
    @loadChildrenComplete="(e) => (collection = e.collection)"
  >
    <TreeView.Label>Tree</TreeView.Label>
    <TreeView.Tree>
      <AsyncTreeNode
        v-for="(node, index) in collection.rootNode.children"
        :key="node.id"
        :node="node"
        :indexPath="[index]"
      />
    </TreeView.Tree>
  </TreeView.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { TreeView, createTreeCollection } from '$lib'
  import { ChevronRightIcon, FileIcon, FolderIcon, LoaderCircleIcon } from 'lucide-svelte'
  import styles from 'styles/tree-view.module.css'

  interface TreeNode {
    id: string
    name: string
    children?: TreeNode[]
    childrenCount?: number
  }

  const response: Record<string, TreeNode[]> = {
    node_modules: [
      { id: 'zag-js', name: 'zag-js' },
      { id: 'pandacss', name: 'panda' },
      { id: '@types', name: '@types', childrenCount: 2 },
    ],
    'node_modules/@types': [
      { id: 'react', name: 'react' },
      { id: 'react-dom', name: 'react-dom' },
    ],
    src: [
      { id: 'app.tsx', name: 'app.tsx' },
      { id: 'index.ts', name: 'index.ts' },
    ],
  }

  function loadChildren(details: TreeView.LoadChildrenDetails<TreeNode>): Promise<TreeNode[]> {
    const value = details.valuePath.join('/')
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(response[value] ?? [])
      }, 1200)
    })
  }

  const initialCollection = createTreeCollection<TreeNode>({
    nodeToValue: (node) => node.id,
    nodeToString: (node) => node.name,
    rootNode: {
      id: 'ROOT',
      name: '',
      children: [
        { id: 'node_modules', name: 'node_modules', childrenCount: 3 },
        { id: 'src', name: 'src', childrenCount: 2 },
        { 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' },
      ],
    },
  })

  let collection = $state(initialCollection)
</script>

<TreeView.Root
  class={styles.Root}
  {collection}
  {loadChildren}
  onLoadChildrenComplete={(e) => (collection = e.collection)}
>
  <TreeView.Label class={styles.Label}>Tree</TreeView.Label>
  <TreeView.Tree class={styles.Tree}>
    {#each collection.rootNode.children ?? [] as node, index (node.id)}
      {@render renderNode(node, [index])}
    {/each}
  </TreeView.Tree>
</TreeView.Root>

{#snippet renderNode(node: TreeNode, indexPath: number[])}
  <TreeView.NodeProvider {node} {indexPath}>
    <TreeView.NodeContext>
      {#snippet render(nodeState)}
        {#if node.children || node.childrenCount}
          <TreeView.Branch class={styles.Branch}>
            <TreeView.BranchControl class={styles.BranchControl}>
              <TreeView.BranchIndicator class={styles.BranchIndicator}>
                <ChevronRightIcon />
              </TreeView.BranchIndicator>
              <TreeView.BranchText class={styles.BranchText}>
                {#if nodeState().loading}
                  <LoaderCircleIcon style="animation: spin 1s infinite" />
                {:else}
                  <FolderIcon />
                {/if}
                {node.name}
              </TreeView.BranchText>
            </TreeView.BranchControl>
            <TreeView.BranchContent class={styles.BranchContent}>
              <TreeView.BranchIndentGuide class={styles.BranchIndentGuide} />
              {#each node.children ?? [] as child, index (child.id)}
                {@render renderNode(child, [...indexPath, index])}
              {/each}
            </TreeView.BranchContent>
          </TreeView.Branch>
        {:else}
          <TreeView.Item class={styles.Item}>
            <TreeView.ItemText class={styles.ItemText}>
              <FileIcon />
              {node.name}
            </TreeView.ItemText>
          </TreeView.Item>
        {/if}
      {/snippet}
    </TreeView.NodeContext>
  </TreeView.NodeProvider>
{/snippet}

```

### Lazy Mount

Lazy mounting is a feature that allows the content of a tree view to be rendered only when it is expanded. This is
useful for performance optimization, especially when tree content is large or complex. To enable lazy mounting, use the
`lazyMount` prop on the `TreeView.Root` component.

In addition, the `unmountOnExit` prop can be used in conjunction with `lazyMount` to unmount the tree view content when
branches are collapsed, freeing up resources. The next time a branch is expanded, its content will be re-rendered.

**Example: lazy-mount**

#### React

```tsx
import { TreeView, createTreeCollection } from '@ark-ui/react/tree-view'
import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon } from 'lucide-react'
import { useTreeViewNodeContext } from '../use-tree-view-node-context'
import styles from 'styles/tree-view.module.css'

export const LazyMount = () => {
  return (
    <TreeView.Root className={styles.Root} collection={collection} lazyMount unmountOnExit>
      <TreeView.Label className={styles.Label}>Tree</TreeView.Label>
      <TreeView.Tree className={styles.Tree}>
        {collection.rootNode.children?.map((node, index) => (
          <TreeNode key={node.id} node={node} indexPath={[index]} />
        ))}
      </TreeView.Tree>
    </TreeView.Root>
  )
}

const TreeBranchIcon = () => {
  const { expanded } = useTreeViewNodeContext()
  return expanded ? <FolderOpenIcon /> : <FolderIcon />
}

const TreeNode = (props: TreeView.NodeProviderProps<Node>) => {
  const { node, indexPath } = props
  return (
    <TreeView.NodeProvider key={node.id} node={node} indexPath={indexPath}>
      {node.children ? (
        <TreeView.Branch className={styles.Branch}>
          <TreeView.BranchControl className={styles.BranchControl}>
            <TreeView.BranchIndicator className={styles.BranchIndicator}>
              <ChevronRightIcon />
            </TreeView.BranchIndicator>
            <TreeView.BranchText className={styles.BranchText}>
              <TreeBranchIcon /> {node.name}
            </TreeView.BranchText>
          </TreeView.BranchControl>
          <TreeView.BranchContent className={styles.BranchContent}>
            <TreeView.BranchIndentGuide className={styles.BranchIndentGuide} />
            {node.children.map((child, index) => (
              <TreeNode key={child.id} node={child} indexPath={[...indexPath, index]} />
            ))}
          </TreeView.BranchContent>
        </TreeView.Branch>
      ) : (
        <TreeView.Item className={styles.Item}>
          <TreeView.ItemText className={styles.ItemText}>
            <FileIcon />
            {node.name}
          </TreeView.ItemText>
        </TreeView.Item>
      )}
    </TreeView.NodeProvider>
  )
}

interface Node {
  id: string
  name: string
  children?: Node[] | undefined
}

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' },
    ],
  },
})

```

#### Solid

```tsx
import { TreeView, createTreeCollection } from '@ark-ui/solid/tree-view'
import { SquareCheckBigIcon, 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 LazyMount = () => {
  return (
    <TreeView.Root collection={collection} lazyMount unmountOnExit>
      <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>
              <SquareCheckBigIcon />
            </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>
  )
}

```

#### Vue

```vue
<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" lazy-mount unmount-on-exit>
    <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>

```

#### Svelte

```svelte
<script lang="ts">
  import { TreeView, createTreeCollection } from '$lib'
  import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon } from 'lucide-svelte'
  import styles from 'styles/tree-view.module.css'

  interface TreeNode {
    id: string
    name: string
    children?: TreeNode[]
  }

  const collection = createTreeCollection<TreeNode>({
    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>

<TreeView.Root class={styles.Root} {collection} lazyMount unmountOnExit>
  <TreeView.Label class={styles.Label}>Tree</TreeView.Label>
  <TreeView.Tree class={styles.Tree}>
    {#each collection.rootNode.children ?? [] as node, index (node.id)}
      {@render renderNode(node, [index])}
    {/each}
  </TreeView.Tree>
</TreeView.Root>

{#snippet renderNode(node: TreeNode, indexPath: number[])}
  <TreeView.NodeProvider {node} {indexPath}>
    <TreeView.NodeContext>
      {#snippet render(nodeState)}
        {#if node.children}
          <TreeView.Branch class={styles.Branch}>
            <TreeView.BranchControl class={styles.BranchControl}>
              <TreeView.BranchIndicator class={styles.BranchIndicator}>
                <ChevronRightIcon />
              </TreeView.BranchIndicator>
              <TreeView.BranchText class={styles.BranchText}>
                {#if nodeState().expanded}
                  <FolderOpenIcon />
                {:else}
                  <FolderIcon />
                {/if}
                {node.name}
              </TreeView.BranchText>
            </TreeView.BranchControl>
            <TreeView.BranchContent class={styles.BranchContent}>
              <TreeView.BranchIndentGuide class={styles.BranchIndentGuide} />
              {#each node.children as child, index (child.id)}
                {@render renderNode(child, [...indexPath, index])}
              {/each}
            </TreeView.BranchContent>
          </TreeView.Branch>
        {:else}
          <TreeView.Item class={styles.Item}>
            <TreeView.ItemText class={styles.ItemText}>
              <FileIcon />
              {node.name}
            </TreeView.ItemText>
          </TreeView.Item>
        {/if}
      {/snippet}
    </TreeView.NodeContext>
  </TreeView.NodeProvider>
{/snippet}

```

### Filtering

Filtering is useful when you have a large tree and you want to filter the nodes to only show the ones that match the
search query. Here's an example that composes the `filter` method from the `TreeCollection` and `useFilter` hook to
filter the nodes.

**Example: filtering**

#### React

```tsx
import { useFilter } from '@ark-ui/react/locale'
import { TreeView, createTreeCollection, useTreeViewContext } from '@ark-ui/react/tree-view'
import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon } from 'lucide-react'
import { useState } from 'react'
import styles from 'styles/tree-view.module.css'
import fieldStyles from 'styles/field.module.css'

export const Filtering = () => {
  const { contains } = useFilter({ sensitivity: 'base' })
  const [collection, setCollection] = useState(initialCollection)

  const filter = (value: string) => {
    const filtered =
      value.length > 0 ? initialCollection.filter((node) => contains(node.name, value)) : initialCollection
    setCollection(filtered)
  }

  return (
    <div className="stack" style={{ maxWidth: '20rem' }}>
      <input className={fieldStyles.Input} placeholder="Search" onChange={(e) => filter(e.target.value)} />
      <TreeView.Root className={styles.Root} collection={collection}>
        <TreeView.Tree className={styles.Tree}>
          {collection.rootNode.children?.map((node, index) => (
            <TreeNode key={node.id} node={node} indexPath={[index]} />
          ))}
        </TreeView.Tree>
      </TreeView.Root>
    </div>
  )
}

const TreeNode = (props: TreeView.NodeProviderProps<Node>) => {
  const { node, indexPath } = props
  const tree = useTreeViewContext()
  const nodeState = tree.getNodeState(props)
  return (
    <TreeView.NodeProvider key={node.id} node={node} indexPath={indexPath}>
      {nodeState.isBranch ? (
        <TreeView.Branch className={styles.Branch}>
          <TreeView.BranchControl className={styles.BranchControl}>
            <TreeView.BranchIndicator className={styles.BranchIndicator}>
              <ChevronRightIcon />
            </TreeView.BranchIndicator>
            <TreeView.BranchText className={styles.BranchText}>
              {nodeState.expanded ? <FolderOpenIcon /> : <FolderIcon />} {node.name}
            </TreeView.BranchText>
          </TreeView.BranchControl>
          <TreeView.BranchContent className={styles.BranchContent}>
            <TreeView.BranchIndentGuide className={styles.BranchIndentGuide} />
            {node.children?.map((child, index) => (
              <TreeNode key={child.id} node={child} indexPath={[...indexPath, index]} />
            ))}
          </TreeView.BranchContent>
        </TreeView.Branch>
      ) : (
        <TreeView.Item className={styles.Item}>
          <TreeView.ItemText className={styles.ItemText}>
            <FileIcon />
            {node.name}
          </TreeView.ItemText>
        </TreeView.Item>
      )}
    </TreeView.NodeProvider>
  )
}

interface Node {
  id: string
  name: string
  children?: Node[]
}

const initialCollection = 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' },
    ],
  },
})

```

#### Solid

```tsx
import { useFilter } from '@ark-ui/solid/locale'
import { TreeView, createTreeCollection } from '@ark-ui/solid/tree-view'
import { SquareCheckBigIcon, ChevronRightIcon, FileIcon, FolderIcon } from 'lucide-solid'
import { For, Show, createSignal } from 'solid-js'

export const Filtering = () => {
  const filterFn = useFilter({ sensitivity: 'base' })
  const [collection, setCollection] = createSignal(initialCollection)

  const filter = (value: string) => {
    const filtered =
      value.length > 0 ? initialCollection.filter((node) => filterFn().contains(node.name, value)) : initialCollection
    setCollection(filtered)
  }

  return (
    <div>
      <input placeholder="Search" onInput={(e) => filter(e.currentTarget.value)} />
      <TreeView.Root collection={collection()}>
        <TreeView.Tree>
          <For each={collection().rootNode.children}>
            {(node, index) => <TreeNode node={node} indexPath={[index()]} />}
          </For>
        </TreeView.Tree>
      </TreeView.Root>
    </div>
  )
}

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>
              <SquareCheckBigIcon />
            </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>
  )
}

interface Node {
  id: string
  name: string
  children?: Node[]
}

const initialCollection = 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' },
    ],
  },
})

```

#### Vue

```vue
<script setup lang="ts">
import { useFilter } from '@ark-ui/vue/locale'
import { TreeView, createTreeCollection } from '@ark-ui/vue/tree-view'
import { shallowRef } from 'vue'
import TreeNode from './tree-node.vue'

interface Node {
  id: string
  name: string
  children?: Node[]
}

const initialCollection = 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' },
    ],
  },
})

const filterFns = useFilter({ sensitivity: 'base' })
const collection = shallowRef(initialCollection)

const filter = (value: string) => {
  const filtered =
    value.length > 0
      ? initialCollection.filter((node) => filterFns.value.contains(node.name, value))
      : initialCollection

  collection.value = filtered
}
</script>

<template>
  <div>
    <input placeholder="Search" @input="(e) => filter((e.currentTarget as HTMLInputElement).value)" />
    <TreeView.Root :collection="collection">
      <TreeView.Tree>
        <TreeNode
          v-for="(node, index) in collection.rootNode.children"
          :key="node.id"
          :node="node"
          :indexPath="[index]"
        />
      </TreeView.Tree>
    </TreeView.Root>
  </div>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { TreeView, createTreeCollection } from '$lib'
  import { useFilter } from '@ark-ui/svelte/locale'
  import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon } from 'lucide-svelte'
  import styles from 'styles/tree-view.module.css'

  interface TreeNode {
    id: string
    name: string
    children?: TreeNode[]
  }

  const initialCollection = createTreeCollection<TreeNode>({
    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' },
      ],
    },
  })

  const filterFns = useFilter({ sensitivity: 'base' })
  let collection = $state(initialCollection)

  const filter = (value: string) => {
    const filtered =
      value.length > 0 ? initialCollection.filter((node) => filterFns().contains(node.name, value)) : initialCollection
    collection = filtered
  }
</script>

<div>
  <input placeholder="Search" oninput={(e) => filter(e.currentTarget.value)} />
  <TreeView.Root class={styles.Root} {collection}>
    <TreeView.Tree class={styles.Tree}>
      {#each collection.rootNode.children ?? [] as node, index (node.id)}
        {@render renderNode(node, [index])}
      {/each}
    </TreeView.Tree>
  </TreeView.Root>
</div>

{#snippet renderNode(node: TreeNode, indexPath: number[])}
  <TreeView.NodeProvider {node} {indexPath}>
    <TreeView.NodeContext>
      {#snippet render(nodeState)}
        {#if node.children}
          <TreeView.Branch class={styles.Branch}>
            <TreeView.BranchControl class={styles.BranchControl}>
              <TreeView.BranchIndicator class={styles.BranchIndicator}>
                <ChevronRightIcon />
              </TreeView.BranchIndicator>
              <TreeView.BranchText class={styles.BranchText}>
                {#if nodeState().expanded}
                  <FolderOpenIcon />
                {:else}
                  <FolderIcon />
                {/if}
                {node.name}
              </TreeView.BranchText>
            </TreeView.BranchControl>
            <TreeView.BranchContent class={styles.BranchContent}>
              <TreeView.BranchIndentGuide class={styles.BranchIndentGuide} />
              {#each node.children as child, index (child.id)}
                {@render renderNode(child, [...indexPath, index])}
              {/each}
            </TreeView.BranchContent>
          </TreeView.Branch>
        {:else}
          <TreeView.Item class={styles.Item}>
            <TreeView.ItemText class={styles.ItemText}>
              <FileIcon />
              {node.name}
            </TreeView.ItemText>
          </TreeView.Item>
        {/if}
      {/snippet}
    </TreeView.NodeContext>
  </TreeView.NodeProvider>
{/snippet}

```

### Links

Tree items can be rendered as links to another page or website. This could be useful for documentation sites.

Here's an example that modifies the tree collection to represent an hierarchical link structure. It uses the `asChild`
prop to render the tree items as links, passing the `href` prop to a `<a>` element.

**Example: links**

#### React

```tsx
import { TreeView, createTreeCollection } from '@ark-ui/react/tree-view'
import { ChevronRightIcon, ExternalLinkIcon, FileIcon } from 'lucide-react'
import styles from 'styles/tree-view.module.css'

export const Links = () => {
  return (
    <TreeView.Root className={styles.Root} collection={collection}>
      <TreeView.Label className={styles.Label}>Docs</TreeView.Label>
      <TreeView.Tree className={styles.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 className={styles.Branch}>
          <TreeView.BranchControl className={styles.BranchControl}>
            <TreeView.BranchIndicator className={styles.BranchIndicator}>
              <ChevronRightIcon />
            </TreeView.BranchIndicator>
            <TreeView.BranchText className={styles.BranchText}>{node.name}</TreeView.BranchText>
          </TreeView.BranchControl>
          <TreeView.BranchContent className={styles.BranchContent}>
            <TreeView.BranchIndentGuide className={styles.BranchIndentGuide} />
            {node.children.map((child, index) => (
              <TreeNode key={child.id} node={child} indexPath={[...indexPath, index]} />
            ))}
          </TreeView.BranchContent>
        </TreeView.Branch>
      ) : (
        <TreeView.Item className={styles.Item} asChild>
          <a href={node.href}>
            <TreeView.ItemText className={styles.ItemText}>
              <FileIcon />
              {node.name}
            </TreeView.ItemText>
            {node.href?.startsWith('http') && <ExternalLinkIcon size={12} />}
          </a>
        </TreeView.Item>
      )}
    </TreeView.NodeProvider>
  )
}

interface Node {
  id: string
  name: string
  href?: string
  children?: Node[]
}

const collection = createTreeCollection<Node>({
  nodeToValue: (node) => node.id,
  nodeToString: (node) => node.name,
  rootNode: {
    id: 'ROOT',
    name: '',
    children: [
      {
        id: 'docs',
        name: 'Documentation',
        children: [
          { id: 'docs/getting-started', name: 'Getting Started', href: '/docs/getting-started' },
          { id: 'docs/installation', name: 'Installation', href: '/docs/installation' },
          {
            id: 'docs/components',
            name: 'Components',
            children: [
              { id: 'docs/components/accordion', name: 'Accordion', href: '/docs/components/accordion' },
              { id: 'docs/components/dialog', name: 'Dialog', href: '/docs/components/dialog' },
              { id: 'docs/components/menu', name: 'Menu', href: '/docs/components/menu' },
            ],
          },
        ],
      },
      {
        id: 'examples',
        name: 'Examples',
        children: [
          { id: 'examples/react', name: 'React Examples', href: '/examples/react' },
          { id: 'examples/vue', name: 'Vue Examples', href: '/examples/vue' },
          { id: 'examples/solid', name: 'Solid Examples', href: '/examples/solid' },
        ],
      },
      {
        id: 'external',
        name: 'External Links',
        children: [
          { id: 'external/github', name: 'GitHub Repository', href: 'https://github.com/chakra-ui/zag' },
          { id: 'external/npm', name: 'NPM Package', href: 'https://www.npmjs.com/package/@zag-js/core' },
          { id: 'external/docs', name: 'Official Docs', href: 'https://zagjs.com' },
        ],
      },
      { id: 'readme.md', name: 'README.md', href: '/readme' },
      { id: 'license', name: 'LICENSE', href: '/license' },
    ],
  },
})

```

#### Solid

```tsx
import { TreeView, createTreeCollection } from '@ark-ui/solid/tree-view'
import { ChevronRightIcon, ExternalLinkIcon, FileIcon } from 'lucide-solid'
import { For, Show } from 'solid-js'

export const Links = () => {
  return (
    <TreeView.Root collection={collection}>
      <TreeView.Label>Docs</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 asChild={(props) => <a href={node.href} {...props()} />}>
            <TreeView.ItemText>
              <FileIcon />
              {node.name}
            </TreeView.ItemText>
            <Show when={node.href?.startsWith('http')}>
              <ExternalLinkIcon size={12} />
            </Show>
          </TreeView.Item>
        }
      >
        <TreeView.Branch>
          <TreeView.BranchControl>
            <TreeView.BranchText>{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>
  )
}

interface Node {
  id: string
  name: string
  href?: string
  children?: Node[]
}

const collection = createTreeCollection<Node>({
  nodeToValue: (node) => node.id,
  nodeToString: (node) => node.name,
  rootNode: {
    id: 'ROOT',
    name: '',
    children: [
      {
        id: 'docs',
        name: 'Documentation',
        children: [
          { id: 'docs/getting-started', name: 'Getting Started', href: '/docs/getting-started' },
          { id: 'docs/installation', name: 'Installation', href: '/docs/installation' },
          {
            id: 'docs/components',
            name: 'Components',
            children: [
              { id: 'docs/components/accordion', name: 'Accordion', href: '/docs/components/accordion' },
              { id: 'docs/components/dialog', name: 'Dialog', href: '/docs/components/dialog' },
              { id: 'docs/components/menu', name: 'Menu', href: '/docs/components/menu' },
            ],
          },
        ],
      },
      {
        id: 'examples',
        name: 'Examples',
        children: [
          { id: 'examples/react', name: 'React Examples', href: '/examples/react' },
          { id: 'examples/vue', name: 'Vue Examples', href: '/examples/vue' },
          { id: 'examples/solid', name: 'Solid Examples', href: '/examples/solid' },
        ],
      },
      {
        id: 'external',
        name: 'External Links',
        children: [
          { id: 'external/github', name: 'GitHub Repository', href: 'https://github.com/chakra-ui/zag' },
          { id: 'external/npm', name: 'NPM Package', href: 'https://www.npmjs.com/package/@zag-js/core' },
          { id: 'external/docs', name: 'Official Docs', href: 'https://zagjs.com' },
        ],
      },
      { id: 'readme.md', name: 'README.md', href: '/readme' },
      { id: 'license', name: 'LICENSE', href: '/license' },
    ],
  },
})

```

#### Vue

```vue
<script setup lang="ts">
import { TreeView, createTreeCollection } from '@ark-ui/vue/tree-view'
import TreeNodeWithLinks from './tree-node-with-links.vue'

interface Node {
  id: string
  name: string
  href?: string
  children?: Node[]
}

const collection = createTreeCollection<Node>({
  nodeToValue: (node) => node.id,
  nodeToString: (node) => node.name,
  rootNode: {
    id: 'ROOT',
    name: '',
    children: [
      {
        id: 'docs',
        name: 'Documentation',
        children: [
          { id: 'docs/getting-started', name: 'Getting Started', href: '/docs/getting-started' },
          { id: 'docs/installation', name: 'Installation', href: '/docs/installation' },
          {
            id: 'docs/components',
            name: 'Components',
            children: [
              { id: 'docs/components/accordion', name: 'Accordion', href: '/docs/components/accordion' },
              { id: 'docs/components/dialog', name: 'Dialog', href: '/docs/components/dialog' },
              { id: 'docs/components/menu', name: 'Menu', href: '/docs/components/menu' },
            ],
          },
        ],
      },
      {
        id: 'examples',
        name: 'Examples',
        children: [
          { id: 'examples/react', name: 'React Examples', href: '/examples/react' },
          { id: 'examples/vue', name: 'Vue Examples', href: '/examples/vue' },
          { id: 'examples/solid', name: 'Solid Examples', href: '/examples/solid' },
        ],
      },
      {
        id: 'external',
        name: 'External Links',
        children: [
          { id: 'external/github', name: 'GitHub Repository', href: 'https://github.com/chakra-ui/zag' },
          { id: 'external/npm', name: 'NPM Package', href: 'https://www.npmjs.com/package/@zag-js/core' },
          { id: 'external/docs', name: 'Official Docs', href: 'https://zagjs.com' },
        ],
      },
      { id: 'readme.md', name: 'README.md', href: '/readme' },
      { id: 'license', name: 'LICENSE', href: '/license' },
    ],
  },
})
</script>

<template>
  <TreeView.Root :collection="collection">
    <TreeView.Label>Docs</TreeView.Label>
    <TreeView.Tree>
      <TreeNodeWithLinks
        v-for="(node, index) in collection.rootNode.children"
        :key="node.id"
        :node="node"
        :indexPath="[index]"
      />
    </TreeView.Tree>
  </TreeView.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { TreeView, createTreeCollection } from '$lib'
  import LinksTreeNode from './links-tree-node.svelte'
  import styles from 'styles/tree-view.module.css'

  interface TreeNode {
    id: string
    name: string
    href?: string
    children?: TreeNode[]
  }

  const collection = createTreeCollection<TreeNode>({
    nodeToValue: (node) => node.id,
    nodeToString: (node) => node.name,
    rootNode: {
      id: 'ROOT',
      name: '',
      children: [
        {
          id: 'docs',
          name: 'Documentation',
          children: [
            { id: 'docs/getting-started', name: 'Getting Started', href: '/docs/getting-started' },
            { id: 'docs/installation', name: 'Installation', href: '/docs/installation' },
            {
              id: 'docs/components',
              name: 'Components',
              children: [
                { id: 'docs/components/accordion', name: 'Accordion', href: '/docs/components/accordion' },
                { id: 'docs/components/dialog', name: 'Dialog', href: '/docs/components/dialog' },
                { id: 'docs/components/menu', name: 'Menu', href: '/docs/components/menu' },
              ],
            },
          ],
        },
        {
          id: 'examples',
          name: 'Examples',
          children: [
            { id: 'examples/react', name: 'React Examples', href: '/examples/react' },
            { id: 'examples/vue', name: 'Vue Examples', href: '/examples/vue' },
            { id: 'examples/solid', name: 'Solid Examples', href: '/examples/solid' },
          ],
        },
        {
          id: 'external',
          name: 'External Links',
          children: [
            { id: 'external/github', name: 'GitHub Repository', href: 'https://github.com/chakra-ui/zag' },
            { id: 'external/npm', name: 'NPM Package', href: 'https://www.npmjs.com/package/@zag-js/core' },
            { id: 'external/docs', name: 'Official Docs', href: 'https://zagjs.com' },
          ],
        },
        { id: 'readme.md', name: 'README.md', href: '/readme' },
        { id: 'license', name: 'LICENSE', href: '/license' },
      ],
    },
  })
</script>

<TreeView.Root class={styles.Root} {collection}>
  <TreeView.Label class={styles.Label}>Docs</TreeView.Label>
  <TreeView.Tree class={styles.Tree}>
    {#each collection.rootNode.children ?? [] as node, index (node.id)}
      <LinksTreeNode {node} indexPath={[index]} />
    {/each}
  </TreeView.Tree>
</TreeView.Root>

```

### Virtualized

For large tree views with thousands of nodes, virtualization can significantly improve performance by only rendering
visible nodes.

Key implementation details:

- Use `useTreeView` hook with `TreeView.RootProvider` for programmatic control
- Pass `scrollToIndexFn` to enable keyboard navigation within the virtualized list
- Use `getVisibleNodes()` to get the flattened list of currently visible nodes

**Example: virtualized**

#### React

```tsx
import { TreeView, createTreeCollection, useTreeView } from '@ark-ui/react/tree-view'
import { useVirtualizer, type Virtualizer } from '@tanstack/react-virtual'
import { ChevronRightIcon, FileIcon, FolderIcon } from 'lucide-react'
import { useRef } from 'react'
import { flushSync } from 'react-dom'
import button from 'styles/button.module.css'
import styles from 'styles/tree-view.module.css'

interface Node {
  id: string
  name: string
  children?: Node[]
}

function generateLargeTree(): Node {
  const folders: Node[] = []
  for (let i = 0; i < 50; i++) {
    const children: Node[] = []
    for (let j = 0; j < 20; j++) {
      children.push({ id: `folder-${i}/file-${i}-${j}.ts`, name: `file-${i}-${j}.ts` })
    }
    folders.push({ id: `folder-${i}`, name: `folder-${i}`, children })
  }
  return {
    id: 'ROOT',
    name: '',
    children: folders,
  }
}

const collection = createTreeCollection<Node>({
  nodeToValue: (node) => node.id,
  nodeToString: (node) => node.name,
  rootNode: generateLargeTree(),
})

const ROW_HEIGHT = 32

export const Virtualized = () => {
  const treeRef = useRef<HTMLDivElement | null>(null)
  const virtualizerRef = useRef<Virtualizer<HTMLDivElement, Element> | null>(null)

  const tree = useTreeView({
    collection,
    scrollToIndexFn(details) {
      flushSync(() => {
        virtualizerRef.current?.scrollToIndex(details.index, { align: 'auto' })
      })
    },
  })

  const visibleNodes = tree.getVisibleNodes()

  const virtualizer = useVirtualizer({
    count: visibleNodes.length,
    getScrollElement: () => treeRef.current,
    estimateSize: () => ROW_HEIGHT,
    overscan: 10,
  })

  virtualizerRef.current = virtualizer

  return (
    <TreeView.RootProvider className={styles.Root} value={tree}>
      <TreeView.Label className={styles.Label}>Virtualized Tree ({visibleNodes.length} visible nodes)</TreeView.Label>
      <div className="hstack">
        <button className={button.Root} onClick={() => tree.collapse()}>
          Collapse all
        </button>
        <button className={button.Root} onClick={() => tree.expand()}>
          Expand all
        </button>
      </div>
      <TreeView.Tree className={styles.Tree} ref={treeRef} style={{ height: 400, overflow: 'auto' }}>
        <div style={{ minHeight: virtualizer.getTotalSize(), width: '100%', position: 'relative' }}>
          {virtualizer.getVirtualItems().map((virtualItem) => {
            const { node, indexPath } = visibleNodes[virtualItem.index]
            const nodeState = tree.getNodeState({ node, indexPath })

            return (
              <div
                key={node.id}
                data-index={virtualItem.index}
                onPointerDown={(e) => {
                  if (e.button !== 0) return
                  tree.focus(node.id)
                }}
                style={{
                  position: 'absolute',
                  top: 0,
                  left: 0,
                  width: '100%',
                  height: `${virtualItem.size}px`,
                  transform: `translateY(${virtualItem.start}px)`,
                }}
              >
                <TreeView.NodeProvider node={node} indexPath={indexPath}>
                  {nodeState.isBranch ? (
                    <TreeView.BranchControl
                      className={styles.BranchControl}
                      style={{ paddingLeft: nodeState.depth * 22 }}
                    >
                      <TreeView.BranchIndicator className={styles.BranchIndicator}>
                        <ChevronRightIcon />
                      </TreeView.BranchIndicator>
                      <TreeView.BranchText className={styles.BranchText}>
                        <FolderIcon /> {node.name}
                      </TreeView.BranchText>
                    </TreeView.BranchControl>
                  ) : (
                    <TreeView.Item className={styles.Item} style={{ paddingLeft: nodeState.depth * 22 }}>
                      <TreeView.ItemText className={styles.ItemText}>
                        <FileIcon /> {node.name}
                      </TreeView.ItemText>
                    </TreeView.Item>
                  )}
                </TreeView.NodeProvider>
              </div>
            )
          })}
        </div>
      </TreeView.Tree>
    </TreeView.RootProvider>
  )
}

```

#### Solid

```tsx
import { TreeView, createTreeCollection, useTreeView } from '@ark-ui/solid/tree-view'
import { createVirtualizer, type Virtualizer } from '@tanstack/solid-virtual'
import { ChevronRightIcon, FileIcon, FolderIcon } from 'lucide-solid'
import { For } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/tree-view.module.css'

interface Node {
  id: string
  name: string
  children?: Node[]
}

function generateLargeTree(): Node {
  const folders: Node[] = []
  for (let i = 0; i < 50; i++) {
    const children: Node[] = []
    for (let j = 0; j < 20; j++) {
      children.push({ id: `folder-${i}/file-${i}-${j}.ts`, name: `file-${i}-${j}.ts` })
    }
    folders.push({ id: `folder-${i}`, name: `folder-${i}`, children })
  }
  return {
    id: 'ROOT',
    name: '',
    children: folders,
  }
}

const collection = createTreeCollection<Node>({
  nodeToValue: (node) => node.id,
  nodeToString: (node) => node.name,
  rootNode: generateLargeTree(),
})

const ROW_HEIGHT = 32

export const Virtualized = () => {
  let treeRef: HTMLDivElement | undefined
  let virtualizerRef: Virtualizer<HTMLDivElement, Element> | undefined

  const tree = useTreeView({
    collection,
    scrollToIndexFn(details) {
      virtualizerRef?.scrollToIndex(details.index, { align: 'auto' })
    },
  })

  const visibleNodes = () => tree().getVisibleNodes()

  const virtualizer = createVirtualizer({
    get count() {
      return visibleNodes().length
    },
    getScrollElement: () => treeRef ?? null,
    estimateSize: () => ROW_HEIGHT,
    overscan: 10,
  })

  virtualizerRef = virtualizer

  return (
    <TreeView.RootProvider class={styles.Root} value={tree}>
      <TreeView.Label class={styles.Label}>Virtualized Tree ({visibleNodes().length} visible nodes)</TreeView.Label>
      <div class="hstack">
        <button class={button.Root} onClick={() => tree().collapse()}>
          Collapse all
        </button>
        <button class={button.Root} onClick={() => tree().expand()}>
          Expand all
        </button>
      </div>
      <TreeView.Tree class={styles.Tree} ref={treeRef} style={{ height: '400px', overflow: 'auto' }}>
        <div
          style={{
            'min-height': `${virtualizer.getTotalSize()}px`,
            width: '100%',
            position: 'relative',
          }}
        >
          <For each={virtualizer.getVirtualItems()}>
            {(virtualItem) => {
              const visibleNode = () => visibleNodes()[virtualItem.index]
              const nodeState = () =>
                tree().getNodeState({ node: visibleNode().node, indexPath: visibleNode().indexPath })

              return (
                <div
                  data-index={virtualItem.index}
                  onPointerDown={(e) => {
                    if (e.button !== 0) return
                    tree().focus(visibleNode().node.id)
                  }}
                  style={{
                    position: 'absolute',
                    top: 0,
                    left: 0,
                    width: '100%',
                    height: `${virtualItem.size}px`,
                    transform: `translateY(${virtualItem.start}px)`,
                  }}
                >
                  <TreeView.NodeProvider node={visibleNode().node} indexPath={visibleNode().indexPath}>
                    {nodeState().isBranch ? (
                      <TreeView.BranchControl
                        class={styles.BranchControl}
                        style={{ 'padding-left': `${nodeState().depth * 22}px` }}
                      >
                        <TreeView.BranchIndicator class={styles.BranchIndicator}>
                          <ChevronRightIcon />
                        </TreeView.BranchIndicator>
                        <TreeView.BranchText class={styles.BranchText}>
                          <FolderIcon /> {visibleNode().node.name}
                        </TreeView.BranchText>
                      </TreeView.BranchControl>
                    ) : (
                      <TreeView.Item class={styles.Item} style={{ 'padding-left': `${nodeState().depth * 22}px` }}>
                        <TreeView.ItemText class={styles.ItemText}>
                          <FileIcon /> {visibleNode().node.name}
                        </TreeView.ItemText>
                      </TreeView.Item>
                    )}
                  </TreeView.NodeProvider>
                </div>
              )
            }}
          </For>
        </div>
      </TreeView.Tree>
    </TreeView.RootProvider>
  )
}

```

#### Vue

```vue
<script setup lang="ts">
import { TreeView, createTreeCollection, useTreeView } from '@ark-ui/vue/tree-view'
import { useVirtualizer } from '@tanstack/vue-virtual'
import { ChevronRight, File, Folder } from 'lucide-vue-next'
import button from 'styles/button.module.css'
import styles from 'styles/tree-view.module.css'
import { computed, nextTick, ref } from 'vue'

interface Node {
  id: string
  name: string
  children?: Node[]
}

function generateLargeTree(): Node {
  const folders: Node[] = []
  for (let i = 0; i < 50; i++) {
    const children: Node[] = []
    for (let j = 0; j < 20; j++) {
      children.push({ id: `folder-${i}/file-${i}-${j}.ts`, name: `file-${i}-${j}.ts` })
    }
    folders.push({ id: `folder-${i}`, name: `folder-${i}`, children })
  }
  return {
    id: 'ROOT',
    name: '',
    children: folders,
  }
}

const collection = createTreeCollection<Node>({
  nodeToValue: (node) => node.id,
  nodeToString: (node) => node.name,
  rootNode: generateLargeTree(),
})

const ROW_HEIGHT = 32

const treeRef = ref<HTMLDivElement | null>(null)

const tree = useTreeView({
  collection,
  scrollToIndexFn(details) {
    nextTick(() => {
      virtualizer.value?.scrollToIndex(details.index, { align: 'auto' })
    })
  },
})

const visibleNodes = computed(() => tree.value.getVisibleNodes())

const virtualizer = useVirtualizer(
  computed(() => ({
    count: visibleNodes.value.length,
    getScrollElement: () => treeRef.value,
    estimateSize: () => ROW_HEIGHT,
    overscan: 10,
  })),
)
</script>

<template>
  <TreeView.RootProvider :class="styles.Root" :value="tree">
    <TreeView.Label :class="styles.Label">Virtualized Tree ({{ visibleNodes.length }} visible nodes)</TreeView.Label>
    <div class="hstack">
      <button :class="button.Root" @click="tree.collapse()">Collapse all</button>
      <button :class="button.Root" @click="tree.expand()">Expand all</button>
    </div>
    <TreeView.Tree ref="treeRef" :class="styles.Tree" :style="{ height: '400px', overflow: 'auto' }">
      <div
        :style="{
          minHeight: `${virtualizer.getTotalSize()}px`,
          width: '100%',
          position: 'relative',
        }"
      >
        <div
          v-for="virtualItem in virtualizer.getVirtualItems()"
          :key="visibleNodes[virtualItem.index].node.id"
          :data-index="virtualItem.index"
          @pointerdown="
            (e) => {
              if (e.button !== 0) return
              tree.focus(visibleNodes[virtualItem.index].node.id)
            }
          "
          :style="{
            position: 'absolute',
            top: 0,
            left: 0,
            width: '100%',
            height: `${virtualItem.size}px`,
            transform: `translateY(${virtualItem.start}px)`,
          }"
        >
          <TreeView.NodeProvider
            :node="visibleNodes[virtualItem.index].node"
            :indexPath="visibleNodes[virtualItem.index].indexPath"
          >
            <TreeView.NodeContext v-slot="nodeState">
              <template v-if="nodeState.isBranch">
                <TreeView.BranchControl
                  :class="styles.BranchControl"
                  :style="{ paddingLeft: `${nodeState.depth * 22}px` }"
                >
                  <TreeView.BranchIndicator :class="styles.BranchIndicator">
                    <ChevronRight />
                  </TreeView.BranchIndicator>
                  <TreeView.BranchText :class="styles.BranchText">
                    <Folder />
                    {{ visibleNodes[virtualItem.index].node.name }}
                  </TreeView.BranchText>
                </TreeView.BranchControl>
              </template>
              <template v-else>
                <TreeView.Item :class="styles.Item" :style="{ paddingLeft: `${nodeState.depth * 22}px` }">
                  <TreeView.ItemText :class="styles.ItemText">
                    <File />
                    {{ visibleNodes[virtualItem.index].node.name }}
                  </TreeView.ItemText>
                </TreeView.Item>
              </template>
            </TreeView.NodeContext>
          </TreeView.NodeProvider>
        </div>
      </div>
    </TreeView.Tree>
  </TreeView.RootProvider>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { TreeView, createTreeCollection, useTreeView } from '$lib'
  import { createVirtualizer } from '@tanstack/svelte-virtual'
  import { ChevronRightIcon, FileIcon, FolderIcon } from 'lucide-svelte'
  import button from 'styles/button.module.css'
  import styles from 'styles/tree-view.module.css'
  import { get } from 'svelte/store'

  interface Node {
    id: string
    name: string
    children?: Node[]
  }

  function generateLargeTree(): Node {
    const folders: Node[] = []
    for (let i = 0; i < 50; i++) {
      const children: Node[] = []
      for (let j = 0; j < 20; j++) {
        children.push({ id: `folder-${i}/file-${i}-${j}.ts`, name: `file-${i}-${j}.ts` })
      }
      folders.push({ id: `folder-${i}`, name: `folder-${i}`, children })
    }
    return {
      id: 'ROOT',
      name: '',
      children: folders,
    }
  }

  const collection = createTreeCollection<Node>({
    nodeToValue: (node) => node.id,
    nodeToString: (node) => node.name,
    rootNode: generateLargeTree(),
  })

  const ROW_HEIGHT = 32

  let treeRef = $state<HTMLDivElement | null>(null)

  const id = $props.id()
  const tree = useTreeView({
    collection,
    id,
    scrollToIndexFn(details) {
      get(virtualizer)?.scrollToIndex(details.index, { align: 'auto' })
    },
  })

  const visibleNodes = $derived(tree().getVisibleNodes())

  const virtualizer = createVirtualizer<HTMLDivElement, Element>({
    get count() {
      return visibleNodes.length
    },
    getScrollElement: () => treeRef,
    estimateSize: () => ROW_HEIGHT,
    overscan: 10,
  })
</script>

<TreeView.RootProvider class={styles.Root} value={tree}>
  <TreeView.Label class={styles.Label}>Virtualized Tree ({visibleNodes.length} visible nodes)</TreeView.Label>
  <div class="hstack">
    <button class={button.Root} onclick={() => tree().collapse()}>Collapse all</button>
    <button class={button.Root} onclick={() => tree().expand()}>Expand all</button>
  </div>
  <TreeView.Tree bind:ref={treeRef} class={styles.Tree} style="height: 400px; overflow: auto;">
    <div style="min-height: {$virtualizer.getTotalSize()}px; width: 100%; position: relative;">
      {#each $virtualizer.getVirtualItems() as virtualItem (visibleNodes[virtualItem.index].node.id)}
        {@const visibleNode = visibleNodes[virtualItem.index]}
        {@const nodeState = tree().getNodeState({ node: visibleNode.node, indexPath: visibleNode.indexPath })}
        <div
          data-index={virtualItem.index}
          onpointerdown={(e) => {
            if (e.button !== 0) return
            tree().focus(visibleNode.node.id)
          }}
          style="position: absolute; top: 0; left: 0; width: 100%; height: {virtualItem.size}px; transform: translateY({virtualItem.start}px);"
        >
          <TreeView.NodeProvider node={visibleNode.node} indexPath={visibleNode.indexPath}>
            {#if nodeState.isBranch}
              <TreeView.BranchControl class={styles.BranchControl} style="padding-left: {nodeState.depth * 22}px">
                <TreeView.BranchIndicator class={styles.BranchIndicator}>
                  <ChevronRightIcon />
                </TreeView.BranchIndicator>
                <TreeView.BranchText class={styles.BranchText}>
                  <FolderIcon />
                  {visibleNode.node.name}
                </TreeView.BranchText>
              </TreeView.BranchControl>
            {:else}
              <TreeView.Item class={styles.Item} style="padding-left: {nodeState.depth * 22}px">
                <TreeView.ItemText class={styles.ItemText}>
                  <FileIcon />
                  {visibleNode.node.name}
                </TreeView.ItemText>
              </TreeView.Item>
            {/if}
          </TreeView.NodeProvider>
        </div>
      {/each}
    </div>
  </TreeView.Tree>
</TreeView.RootProvider>

```

### Checkbox Tree

Use the `defaultCheckedValue` prop to enable checkbox selection mode. This allows users to select multiple nodes with
checkboxes, including parent-child selection relationships.

**Example: checkbox-tree**

#### React

```tsx
import { TreeView, createTreeCollection } from '@ark-ui/react/tree-view'
import { CheckIcon, ChevronRightIcon, MinusIcon } from 'lucide-react'
import styles from 'styles/tree-view.module.css'

export const CheckboxTree = () => {
  return (
    <TreeView.Root className={styles.Root} collection={collection} defaultCheckedValue={[]}>
      <TreeView.Label className={styles.Label}>Tree</TreeView.Label>
      <TreeView.Tree className={styles.Tree}>
        {collection.rootNode.children?.map((node, index) => (
          <TreeNode key={node.id} node={node} indexPath={[index]} />
        ))}
      </TreeView.Tree>
    </TreeView.Root>
  )
}

const TreeNodeCheckbox = (props: TreeView.NodeCheckboxProps) => {
  return (
    <TreeView.NodeCheckbox className={styles.NodeCheckbox} {...props}>
      <TreeView.NodeCheckboxIndicator className={styles.NodeCheckboxIndicator} indeterminate={<MinusIcon />}>
        <CheckIcon />
      </TreeView.NodeCheckboxIndicator>
    </TreeView.NodeCheckbox>
  )
}

const TreeNode = (props: TreeView.NodeProviderProps<Node>) => {
  const { node, indexPath } = props
  return (
    <TreeView.NodeProvider key={node.id} node={node} indexPath={indexPath}>
      {node.children ? (
        <TreeView.Branch className={styles.Branch}>
          <TreeView.BranchControl className={styles.BranchControl}>
            <TreeView.BranchIndicator className={styles.BranchIndicator}>
              <ChevronRightIcon />
            </TreeView.BranchIndicator>
            <TreeNodeCheckbox />
            <TreeView.BranchText className={styles.BranchText}>{node.name}</TreeView.BranchText>
          </TreeView.BranchControl>
          <TreeView.BranchContent className={styles.BranchContent}>
            <TreeView.BranchIndentGuide className={styles.BranchIndentGuide} />
            {node.children.map((child, index) => (
              <TreeNode key={child.id} node={child} indexPath={[...indexPath, index]} />
            ))}
          </TreeView.BranchContent>
        </TreeView.Branch>
      ) : (
        <TreeView.Item className={styles.Item}>
          <TreeNodeCheckbox />
          <TreeView.ItemText className={styles.ItemText}>{node.name}</TreeView.ItemText>
        </TreeView.Item>
      )}
    </TreeView.NodeProvider>
  )
}

interface Node {
  id: string
  name: string
  children?: Node[] | undefined
}

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' },
    ],
  },
})

```

#### Solid

```tsx
import { TreeView, createTreeCollection } from '@ark-ui/solid/tree-view'
import { SquareCheckBigIcon, ChevronRightIcon, SquareMinusIcon, SquareIcon } from 'lucide-solid'
import { For } from 'solid-js'

export const CheckboxTree = () => {
  return (
    <TreeView.Root collection={collection} defaultCheckedValue={[]}>
      <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 TreeNodeCheckbox = () => {
  return (
    <TreeView.NodeCheckbox>
      <TreeView.NodeCheckboxIndicator indeterminate={<SquareMinusIcon />} fallback={<SquareIcon />}>
        <SquareCheckBigIcon />
      </TreeView.NodeCheckboxIndicator>
    </TreeView.NodeCheckbox>
  )
}

const TreeNode = (props: TreeView.NodeProviderProps<Node>) => {
  const { node, indexPath } = props
  return (
    <TreeView.NodeProvider node={node} indexPath={indexPath}>
      {node.children ? (
        <TreeView.Branch>
          <TreeView.BranchControl>
            <TreeNodeCheckbox />
            <TreeView.BranchText>{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>
      ) : (
        <TreeView.Item>
          <TreeNodeCheckbox />
          <TreeView.ItemText>{node.name}</TreeView.ItemText>
        </TreeView.Item>
      )}
    </TreeView.NodeProvider>
  )
}

interface Node {
  id: string
  name: string
  children?: Node[] | undefined
}

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' },
    ],
  },
})

```

#### Vue

```vue
<script setup lang="ts">
import { TreeView, createTreeCollection } from '@ark-ui/vue/tree-view'
import TreeNodeWithCheckbox from './tree-node-with-checkbox.vue'

interface Node {
  id: string
  name: string
  children?: Node[] | undefined
}

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" :default-checked-value="[]">
    <TreeView.Label>Tree</TreeView.Label>
    <TreeView.Tree>
      <TreeNodeWithCheckbox
        v-for="(node, index) in collection.rootNode.children"
        :key="node.id"
        :node="node"
        :index-path="[index]"
      />
    </TreeView.Tree>
  </TreeView.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { TreeView, createTreeCollection } from '$lib'
  import { SquareCheckBigIcon, ChevronRightIcon, SquareMinusIcon, SquareIcon } from 'lucide-svelte'
  import styles from 'styles/tree-view.module.css'

  interface TreeNode {
    id: string
    name: string
    children?: TreeNode[]
  }

  const collection = createTreeCollection<TreeNode>({
    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>

<TreeView.Root class={styles.Root} {collection} defaultCheckedValue={[]}>
  <TreeView.Label class={styles.Label}>Tree</TreeView.Label>
  <TreeView.Tree class={styles.Tree}>
    {#each collection.rootNode.children ?? [] as node, index (node.id)}
      {@render renderNode(node, [index])}
    {/each}
  </TreeView.Tree>
</TreeView.Root>

{#snippet treeNodeCheckbox()}
  <TreeView.NodeCheckbox class={styles.NodeCheckbox}>
    <TreeView.NodeCheckboxIndicator>
      {#snippet indeterminate()}
        <SquareMinusIcon />
      {/snippet}
      {#snippet fallback()}
        <SquareIcon />
      {/snippet}
      <SquareCheckBigIcon />
    </TreeView.NodeCheckboxIndicator>
  </TreeView.NodeCheckbox>
{/snippet}

{#snippet renderNode(node: TreeNode, indexPath: number[])}
  <TreeView.NodeProvider {node} {indexPath}>
    {#if node.children}
      <TreeView.Branch class={styles.Branch}>
        <TreeView.BranchControl class={styles.BranchControl}>
          {@render treeNodeCheckbox()}
          <TreeView.BranchText class={styles.BranchText}>{node.name}</TreeView.BranchText>
          <TreeView.BranchIndicator class={styles.BranchIndicator}>
            <ChevronRightIcon />
          </TreeView.BranchIndicator>
        </TreeView.BranchControl>
        <TreeView.BranchContent class={styles.BranchContent}>
          <TreeView.BranchIndentGuide class={styles.BranchIndentGuide} />
          {#each node.children as child, index (child.id)}
            {@render renderNode(child, [...indexPath, index])}
          {/each}
        </TreeView.BranchContent>
      </TreeView.Branch>
    {:else}
      <TreeView.Item class={styles.Item}>
        {@render treeNodeCheckbox()}
        <TreeView.ItemText class={styles.ItemText}>{node.name}</TreeView.ItemText>
      </TreeView.Item>
    {/if}
  </TreeView.NodeProvider>
{/snippet}

```

### Expand and Collapse All

Use the `expand()` and `collapse()` methods from the tree view context to programmatically expand or collapse all
branches.

**Example: expand-collapse-all**

#### React

```tsx
import { TreeView, createTreeCollection, useTreeViewContext } from '@ark-ui/react/tree-view'
import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon } from 'lucide-react'
import { useMemo } from 'react'
import button from 'styles/button.module.css'
import styles from 'styles/tree-view.module.css'

const ExpandCollapseButtons = () => {
  const tree = useTreeViewContext()
  const branchValues = useMemo(() => tree.collection.getBranchValues(), [tree.collection])
  const isAllExpanded = useMemo(
    () => branchValues.every((value) => tree.expandedValue.includes(value)),
    [tree.expandedValue, branchValues],
  )

  return (
    <div className="hstack">
      {isAllExpanded ? (
        <button className={button.Root} onClick={() => tree.collapse()}>
          Collapse all
        </button>
      ) : (
        <button className={button.Root} onClick={() => tree.expand()}>
          Expand all
        </button>
      )}
    </div>
  )
}

export const ExpandCollapseAll = () => {
  return (
    <TreeView.Root className={styles.Root} collection={collection} data-animate="false">
      <ExpandCollapseButtons />
      <TreeView.Tree className={styles.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}>
      <TreeView.NodeContext>
        {(nodeState) =>
          node.children ? (
            <TreeView.Branch className={styles.Branch}>
              <TreeView.BranchControl className={styles.BranchControl}>
                <TreeView.BranchIndicator className={styles.BranchIndicator}>
                  <ChevronRightIcon />
                </TreeView.BranchIndicator>
                <TreeView.BranchText className={styles.BranchText}>
                  {nodeState.expanded ? <FolderOpenIcon /> : <FolderIcon />}
                  {node.name}
                </TreeView.BranchText>
              </TreeView.BranchControl>
              <TreeView.BranchContent className={styles.BranchContent}>
                <TreeView.BranchIndentGuide className={styles.BranchIndentGuide} />
                {node.children.map((child, index) => (
                  <TreeNode key={child.id} node={child} indexPath={[...indexPath, index]} />
                ))}
              </TreeView.BranchContent>
            </TreeView.Branch>
          ) : (
            <TreeView.Item className={styles.Item}>
              <TreeView.ItemText className={styles.ItemText}>
                <FileIcon />
                {node.name}
              </TreeView.ItemText>
            </TreeView.Item>
          )
        }
      </TreeView.NodeContext>
    </TreeView.NodeProvider>
  )
}

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' },
    ],
  },
})

```

#### Solid

```tsx
import { TreeView, createTreeCollection, useTreeViewContext } from '@ark-ui/solid/tree-view'
import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon } from 'lucide-solid'
import { For, Show, createMemo } from 'solid-js'
import button from 'styles/button.module.css'
import styles from 'styles/tree-view.module.css'

const ExpandCollapseButtons = () => {
  const tree = useTreeViewContext()
  const branchValues = createMemo(() => tree().collection.getBranchValues())
  const isAllExpanded = createMemo(() => branchValues().every((value) => tree().expandedValue.includes(value)))

  return (
    <div class="hstack">
      <Show
        when={isAllExpanded()}
        fallback={
          <button class={button.Root} onClick={() => tree().expand()}>
            Expand all
          </button>
        }
      >
        <button class={button.Root} onClick={() => tree().collapse()}>
          Collapse all
        </button>
      </Show>
    </div>
  )
}

export const ExpandCollapseAll = () => {
  return (
    <TreeView.Root class={styles.Root} collection={collection} data-animate="false">
      <ExpandCollapseButtons />
      <TreeView.Tree class={styles.Tree}>
        <For each={collection.rootNode.children}>{(node, index) => <TreeNode node={node} indexPath={[index()]} />}</For>
      </TreeView.Tree>
    </TreeView.Root>
  )
}

const TreeNode = (props: TreeView.NodeProviderProps<Node>) => {
  return (
    <TreeView.NodeProvider node={props.node} indexPath={props.indexPath}>
      <TreeView.NodeContext>
        {(nodeState) => (
          <Show
            when={props.node.children}
            fallback={
              <TreeView.Item class={styles.Item}>
                <TreeView.ItemText class={styles.ItemText}>
                  <FileIcon />
                  {props.node.name}
                </TreeView.ItemText>
              </TreeView.Item>
            }
          >
            <TreeView.Branch class={styles.Branch}>
              <TreeView.BranchControl class={styles.BranchControl}>
                <TreeView.BranchIndicator class={styles.BranchIndicator}>
                  <ChevronRightIcon />
                </TreeView.BranchIndicator>
                <TreeView.BranchText class={styles.BranchText}>
                  <Show when={nodeState().expanded} fallback={<FolderIcon />}>
                    <FolderOpenIcon />
                  </Show>
                  {props.node.name}
                </TreeView.BranchText>
              </TreeView.BranchControl>
              <TreeView.BranchContent class={styles.BranchContent}>
                <TreeView.BranchIndentGuide class={styles.BranchIndentGuide} />
                <For each={props.node.children}>
                  {(child, index) => <TreeNode node={child} indexPath={[...props.indexPath, index()]} />}
                </For>
              </TreeView.BranchContent>
            </TreeView.Branch>
          </Show>
        )}
      </TreeView.NodeContext>
    </TreeView.NodeProvider>
  )
}

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' },
    ],
  },
})

```

#### Vue

```vue
<script setup lang="ts">
import { TreeView, createTreeCollection } from '@ark-ui/vue/tree-view'
import ExpandCollapseTreeNode from './expand-collapse-tree-node.vue'
import button from 'styles/button.module.css'
import styles from 'styles/tree-view.module.css'

interface TreeNode {
  id: string
  name: string
  children?: TreeNode[]
}

const collection = createTreeCollection<TreeNode>({
  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 :class="styles.Root" :collection="collection" data-animate="false">
    <TreeView.Context v-slot="tree">
      <div class="hstack">
        <button
          v-if="collection.getBranchValues().every((value) => tree.expandedValue.includes(value))"
          :class="button.Root"
          @click="tree.collapse()"
        >
          Collapse all
        </button>
        <button v-else :class="button.Root" @click="tree.expand()">Expand all</button>
      </div>
    </TreeView.Context>
    <TreeView.Tree :class="styles.Tree">
      <ExpandCollapseTreeNode
        v-for="(node, index) in collection.rootNode.children"
        :key="node.id"
        :node="node"
        :indexPath="[index]"
      />
    </TreeView.Tree>
  </TreeView.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { TreeView, createTreeCollection } from '$lib'
  import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon } from 'lucide-svelte'
  import button from 'styles/button.module.css'
  import styles from 'styles/tree-view.module.css'

  interface TreeNode {
    id: string
    name: string
    children?: TreeNode[]
  }

  const collection = createTreeCollection<TreeNode>({
    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' },
      ],
    },
  })

  const branchValues = collection.getBranchValues()
</script>

<TreeView.Root class={styles.Root} {collection} data-animate="false">
  <TreeView.Context>
    {#snippet render(tree)}
      <div class="hstack">
        {#if branchValues.every((value) => tree().expandedValue.includes(value))}
          <button class={button.Root} onclick={() => tree().collapse()}>Collapse all</button>
        {:else}
          <button class={button.Root} onclick={() => tree().expand()}>Expand all</button>
        {/if}
      </div>
    {/snippet}
  </TreeView.Context>
  <TreeView.Tree class={styles.Tree}>
    {#each collection.rootNode.children ?? [] as node, index (node.id)}
      {@render renderNode(node, [index])}
    {/each}
  </TreeView.Tree>
</TreeView.Root>

{#snippet renderNode(node: TreeNode, indexPath: number[])}
  <TreeView.NodeProvider {node} {indexPath}>
    <TreeView.NodeContext>
      {#snippet render(nodeState)}
        {#if node.children}
          <TreeView.Branch class={styles.Branch}>
            <TreeView.BranchControl class={styles.BranchControl}>
              <TreeView.BranchIndicator class={styles.BranchIndicator}>
                <ChevronRightIcon />
              </TreeView.BranchIndicator>
              <TreeView.BranchText class={styles.BranchText}>
                {#if nodeState().expanded}
                  <FolderOpenIcon />
                {:else}
                  <FolderIcon />
                {/if}
                {node.name}
              </TreeView.BranchText>
            </TreeView.BranchControl>
            <TreeView.BranchContent class={styles.BranchContent}>
              <TreeView.BranchIndentGuide class={styles.BranchIndentGuide} />
              {#each node.children as child, index (child.id)}
                {@render renderNode(child, [...indexPath, index])}
              {/each}
            </TreeView.BranchContent>
          </TreeView.Branch>
        {:else}
          <TreeView.Item class={styles.Item}>
            <TreeView.ItemText class={styles.ItemText}>
              <FileIcon />
              {node.name}
            </TreeView.ItemText>
          </TreeView.Item>
        {/if}
      {/snippet}
    </TreeView.NodeContext>
  </TreeView.NodeProvider>
{/snippet}

```

### Mutation

Use the collection's `remove()` and `replace()` methods to dynamically add and remove nodes from the tree. This is
useful for building file explorer interfaces where users can create and delete files.

**Example: mutation**

#### React

```tsx
import { TreeView, createTreeCollection, useTreeViewContext } from '@ark-ui/react/tree-view'
import { ChevronRightIcon, PlusIcon, TrashIcon } from 'lucide-react'
import { useState } from 'react'
import styles from 'styles/tree-view.module.css'

export const Mutation = () => {
  const [collection, setCollection] = useState(initialCollection)

  const removeNode = (props: TreeNodeProps) => {
    setCollection(collection.remove([props.indexPath]))
  }

  const addNode = (props: TreeNodeProps) => {
    const { node, indexPath } = props
    if (!collection.isBranchNode(node)) return
    const children = [{ id: `untitled-${Date.now()}`, name: 'untitled.tsx' }, ...(node.children || [])]
    setCollection(collection.replace(indexPath, { ...node, children }))
  }

  return (
    <TreeView.Root className={styles.Root} collection={collection}>
      <TreeView.Tree className={styles.Tree}>
        {collection.rootNode.children?.map((node, index) => (
          <TreeNode key={node.id} node={node} indexPath={[index]} onRemove={removeNode} onAdd={addNode} />
        ))}
      </TreeView.Tree>
    </TreeView.Root>
  )
}

const TreeNodeActions = (props: TreeNodeProps) => {
  const { onRemove, onAdd, node } = props
  const tree = useTreeViewContext()
  const isBranch = tree.collection.isBranchNode(node)
  return (
    <div className={styles.ActionGroup}>
      <button
        className={styles.Action}
        onClick={(e) => {
          e.stopPropagation()
          onRemove?.(props)
        }}
      >
        <TrashIcon />
      </button>
      {isBranch && (
        <button
          className={styles.Action}
          onClick={(e) => {
            e.stopPropagation()
            onAdd?.(props)
            tree.expand([node.id])
          }}
        >
          <PlusIcon />
        </button>
      )}
    </div>
  )
}

interface TreeNodeProps extends TreeView.NodeProviderProps<Node> {
  onRemove?: (props: TreeView.NodeProviderProps<Node>) => void
  onAdd?: (props: TreeView.NodeProviderProps<Node>) => void
}

const TreeNode = (props: TreeNodeProps) => {
  const { node, indexPath } = props
  const tree = useTreeViewContext()
  const nodeState = tree.getNodeState(props)
  return (
    <TreeView.NodeProvider key={node.id} node={node} indexPath={indexPath}>
      {nodeState.isBranch ? (
        <TreeView.Branch className={styles.Branch}>
          <TreeView.BranchControl className={styles.BranchControl}>
            <TreeView.BranchIndicator className={styles.BranchIndicator}>
              <ChevronRightIcon />
            </TreeView.BranchIndicator>
            <TreeView.BranchText className={styles.BranchText}>{node.name}</TreeView.BranchText>
            <TreeNodeActions {...props} />
          </TreeView.BranchControl>
          <TreeView.BranchContent className={styles.BranchContent}>
            <TreeView.BranchIndentGuide className={styles.BranchIndentGuide} />
            {node.children?.map((child, index) => (
              <TreeNode
                key={child.id}
                node={child}
                indexPath={[...indexPath, index]}
                onRemove={props.onRemove}
                onAdd={props.onAdd}
              />
            ))}
          </TreeView.BranchContent>
        </TreeView.Branch>
      ) : (
        <TreeView.Item className={styles.Item}>
          <TreeView.ItemText className={styles.ItemText}>{node.name}</TreeView.ItemText>
          <TreeNodeActions {...props} />
        </TreeView.Item>
      )}
    </TreeView.NodeProvider>
  )
}

interface Node {
  id: string
  name: string
  children?: Node[]
}

const initialCollection = 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' },
    ],
  },
})

```

#### Solid

```tsx
import { TreeView, createTreeCollection, useTreeViewContext } from '@ark-ui/solid/tree-view'
import { ChevronRightIcon, PlusIcon, TrashIcon } from 'lucide-solid'
import { For, Show, createSignal } from 'solid-js'
import styles from 'styles/tree-view.module.css'

export const Mutation = () => {
  const [collection, setCollection] = createSignal(initialCollection)

  const removeNode = (props: TreeNodeProps) => {
    setCollection((prev) => prev.remove([props.indexPath]))
  }

  const addNode = (props: TreeNodeProps) => {
    const { node, indexPath } = props
    const col = collection()
    if (!col.isBranchNode(node)) return
    const children = [{ id: `untitled-${Date.now()}`, name: 'untitled.tsx' }, ...(node.children || [])]
    setCollection(col.replace(indexPath, { ...node, children }))
  }

  return (
    <TreeView.Root class={styles.Root} collection={collection()}>
      <TreeView.Tree class={styles.Tree}>
        <For each={collection().rootNode.children}>
          {(node, index) => <TreeNode node={node} indexPath={[index()]} onRemove={removeNode} onAdd={addNode} />}
        </For>
      </TreeView.Tree>
    </TreeView.Root>
  )
}

const TreeNodeActions = (props: TreeNodeProps) => {
  const tree = useTreeViewContext()
  const isBranch = () => tree().collection.isBranchNode(props.node)
  return (
    <div class={styles.ActionGroup}>
      <button
        class={styles.Action}
        onClick={(e) => {
          e.stopPropagation()
          props.onRemove?.(props)
        }}
      >
        <TrashIcon />
      </button>
      <Show when={isBranch()}>
        <button
          class={styles.Action}
          onClick={(e) => {
            e.stopPropagation()
            props.onAdd?.(props)
            tree().expand([props.node.id])
          }}
        >
          <PlusIcon />
        </button>
      </Show>
    </div>
  )
}

interface TreeNodeProps extends TreeView.NodeProviderProps<Node> {
  onRemove?: (props: TreeView.NodeProviderProps<Node>) => void
  onAdd?: (props: TreeView.NodeProviderProps<Node>) => void
}

const TreeNode = (props: TreeNodeProps) => {
  const tree = useTreeViewContext()
  const nodeState = () => tree().getNodeState(props)
  return (
    <TreeView.NodeProvider node={props.node} indexPath={props.indexPath}>
      <Show
        when={nodeState().isBranch}
        fallback={
          <TreeView.Item class={styles.Item}>
            <TreeView.ItemText class={styles.ItemText}>{props.node.name}</TreeView.ItemText>
            <TreeNodeActions {...props} />
          </TreeView.Item>
        }
      >
        <TreeView.Branch class={styles.Branch}>
          <TreeView.BranchControl class={styles.BranchControl}>
            <TreeView.BranchIndicator class={styles.BranchIndicator}>
              <ChevronRightIcon />
            </TreeView.BranchIndicator>
            <TreeView.BranchText class={styles.BranchText}>{props.node.name}</TreeView.BranchText>
            <TreeNodeActions {...props} />
          </TreeView.BranchControl>
          <TreeView.BranchContent class={styles.BranchContent}>
            <TreeView.BranchIndentGuide class={styles.BranchIndentGuide} />
            <For each={props.node.children}>
              {(child, index) => (
                <TreeNode
                  node={child}
                  indexPath={[...props.indexPath, index()]}
                  onRemove={props.onRemove}
                  onAdd={props.onAdd}
                />
              )}
            </For>
          </TreeView.BranchContent>
        </TreeView.Branch>
      </Show>
    </TreeView.NodeProvider>
  )
}

interface Node {
  id: string
  name: string
  children?: Node[]
}

const initialCollection = 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' },
    ],
  },
})

```

#### Vue

```vue
<script setup lang="ts">
import { TreeView, createTreeCollection } from '@ark-ui/vue/tree-view'
import { shallowRef } from 'vue'
import MutationTreeNode from './mutation-tree-node.vue'
import styles from 'styles/tree-view.module.css'

interface TreeNode {
  id: string
  name: string
  children?: TreeNode[]
}

const collection = shallowRef(
  createTreeCollection<TreeNode>({
    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' },
      ],
    },
  }),
)

const handleRemove = (props: { node: TreeNode; indexPath: number[] }) => {
  collection.value = collection.value.remove([props.indexPath])
}

const handleAdd = (props: { node: TreeNode; indexPath: number[] }) => {
  const { node, indexPath } = props
  if (!collection.value.isBranchNode(node)) return
  const children = [{ id: `untitled-${Date.now()}`, name: 'untitled.tsx' }, ...(node.children || [])]
  collection.value = collection.value.replace(indexPath, { ...node, children })
}
</script>

<template>
  <TreeView.Root :class="styles.Root" :collection="collection">
    <TreeView.Tree :class="styles.Tree">
      <MutationTreeNode
        v-for="(node, index) in collection.rootNode.children"
        :key="node.id"
        :node="node"
        :indexPath="[index]"
        @remove="handleRemove"
        @add="handleAdd"
      />
    </TreeView.Tree>
  </TreeView.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { TreeView, createTreeCollection, type UseTreeViewContext } from '$lib'
  import { ChevronRightIcon, PlusIcon, TrashIcon } from 'lucide-svelte'
  import styles from 'styles/tree-view.module.css'

  interface TreeNode {
    id: string
    name: string
    children?: TreeNode[]
  }

  let collection = $state(
    createTreeCollection<TreeNode>({
      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' },
        ],
      },
    }),
  )

  function handleRemove(node: TreeNode, indexPath: number[]) {
    collection = collection.remove([indexPath])
  }

  function handleAdd(node: TreeNode, indexPath: number[], tree: UseTreeViewContext<TreeNode>) {
    if (!collection.isBranchNode(node)) return
    const children = [{ id: `untitled-${Date.now()}`, name: 'untitled.tsx' }, ...(node.children || [])]
    collection = collection.replace(indexPath, { ...node, children })
    tree().expand([node.id])
  }
</script>

<TreeView.Root class={styles.Root} {collection}>
  <TreeView.Context>
    {#snippet render(tree)}
      <TreeView.Tree class={styles.Tree}>
        {#each collection.rootNode.children ?? [] as node, index (node.id)}
          {@render renderNode(node, [index], tree)}
        {/each}
      </TreeView.Tree>
    {/snippet}
  </TreeView.Context>
</TreeView.Root>

{#snippet renderNode(node: TreeNode, indexPath: number[], tree: any)}
  <TreeView.NodeProvider {node} {indexPath}>
    {#if node.children}
      <TreeView.Branch class={styles.Branch}>
        <TreeView.BranchControl class={styles.BranchControl}>
          <TreeView.BranchIndicator class={styles.BranchIndicator}>
            <ChevronRightIcon />
          </TreeView.BranchIndicator>
          <TreeView.BranchText class={styles.BranchText}>{node.name}</TreeView.BranchText>
          <div class={styles.ActionGroup}>
            <button
              class={styles.Action}
              onclick={(e) => {
                e.stopPropagation()
                handleRemove(node, indexPath)
              }}
            >
              <TrashIcon />
            </button>
            <button
              class={styles.Action}
              onclick={(e) => {
                e.stopPropagation()
                handleAdd(node, indexPath, tree)
              }}
            >
              <PlusIcon />
            </button>
          </div>
        </TreeView.BranchControl>
        <TreeView.BranchContent class={styles.BranchContent}>
          <TreeView.BranchIndentGuide class={styles.BranchIndentGuide} />
          {#each node.children as child, index (child.id)}
            {@render renderNode(child, [...indexPath, index], tree)}
          {/each}
        </TreeView.BranchContent>
      </TreeView.Branch>
    {:else}
      <TreeView.Item class={styles.Item}>
        <TreeView.ItemText class={styles.ItemText}>{node.name}</TreeView.ItemText>
        <div class={styles.ActionGroup}>
          <button
            class={styles.Action}
            onclick={(e) => {
              e.stopPropagation()
              handleRemove(node, indexPath)
            }}
          >
            <TrashIcon />
          </button>
        </div>
      </TreeView.Item>
    {/if}
  </TreeView.NodeProvider>
{/snippet}

```

### Rename Node

Enable inline renaming of nodes using the `canRename` prop and `onRenameComplete` callback. Press <kbd>F2</kbd> to
activate rename mode on the focused node.

**Example: rename-node**

#### React

```tsx
import { TreeView, createTreeCollection } from '@ark-ui/react/tree-view'
import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon } from 'lucide-react'
import { useState } from 'react'

import styles from 'styles/tree-view.module.css'

export const RenameNode = () => {
  const [collection, setCollection] = useState(initialCollection)
  return (
    <TreeView.Root
      className={styles.Root}
      collection={collection}
      canRename={() => true}
      onRenameComplete={(details) => {
        setCollection((prev) => {
          const node = prev.at(details.indexPath)
          if (!node) return prev
          return prev.replace(details.indexPath, { ...node, name: details.label })
        })
      }}
    >
      <TreeView.Label className={styles.Label}>Tree (Press F2 to rename)</TreeView.Label>
      <TreeView.Tree className={styles.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}>
      <TreeView.NodeContext>
        {(nodeState) =>
          node.children ? (
            <TreeView.Branch className={styles.Branch}>
              <TreeView.BranchControl className={styles.BranchControl}>
                <TreeView.BranchIndicator className={styles.BranchIndicator}>
                  <ChevronRightIcon />
                </TreeView.BranchIndicator>
                {nodeState.renaming ? (
                  <TreeView.NodeRenameInput className={styles.NodeRenameInput} />
                ) : (
                  <TreeView.BranchText className={styles.BranchText}>
                    {nodeState.expanded ? <FolderOpenIcon /> : <FolderIcon />}
                    {node.name}
                  </TreeView.BranchText>
                )}
              </TreeView.BranchControl>
              <TreeView.BranchContent className={styles.BranchContent}>
                <TreeView.BranchIndentGuide className={styles.BranchIndentGuide} />
                {node.children.map((child, index) => (
                  <TreeNode key={child.id} node={child} indexPath={[...indexPath, index]} />
                ))}
              </TreeView.BranchContent>
            </TreeView.Branch>
          ) : (
            <TreeView.Item className={styles.Item}>
              <FileIcon />
              {nodeState.renaming ? (
                <TreeView.NodeRenameInput className={styles.NodeRenameInput} />
              ) : (
                <TreeView.ItemText className={styles.ItemText}>{node.name}</TreeView.ItemText>
              )}
            </TreeView.Item>
          )
        }
      </TreeView.NodeContext>
    </TreeView.NodeProvider>
  )
}

interface Node {
  id: string
  name: string
  children?: Node[]
}

const initialCollection = 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' },
    ],
  },
})

```

#### Solid

```tsx
import { TreeView, createTreeCollection } from '@ark-ui/solid/tree-view'
import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon } from 'lucide-solid'
import { For, Show, createSignal } from 'solid-js'
import styles from 'styles/tree-view.module.css'

export const RenameNode = () => {
  const [collection, setCollection] = createSignal(initialCollection)
  return (
    <TreeView.Root
      class={styles.Root}
      collection={collection()}
      canRename={() => true}
      onRenameComplete={(details) => {
        setCollection((prev) => {
          const node = prev.at(details.indexPath)
          if (!node) return prev
          return prev.replace(details.indexPath, { ...node, name: details.label })
        })
      }}
    >
      <TreeView.Label class={styles.Label}>Tree (Press F2 to rename)</TreeView.Label>
      <TreeView.Tree class={styles.Tree}>
        <For each={collection().rootNode.children}>
          {(node, index) => <TreeNode node={node} indexPath={[index()]} />}
        </For>
      </TreeView.Tree>
    </TreeView.Root>
  )
}

const TreeNode = (props: TreeView.NodeProviderProps<Node>) => {
  return (
    <TreeView.NodeProvider node={props.node} indexPath={props.indexPath}>
      <TreeView.NodeContext>
        {(nodeState) => (
          <Show
            when={props.node.children}
            fallback={
              <TreeView.Item class={styles.Item}>
                <FileIcon />
                <Show
                  when={nodeState().renaming}
                  fallback={<TreeView.ItemText class={styles.ItemText}>{props.node.name}</TreeView.ItemText>}
                >
                  <TreeView.NodeRenameInput class={styles.NodeRenameInput} />
                </Show>
              </TreeView.Item>
            }
          >
            <TreeView.Branch class={styles.Branch}>
              <TreeView.BranchControl class={styles.BranchControl}>
                <TreeView.BranchIndicator class={styles.BranchIndicator}>
                  <ChevronRightIcon />
                </TreeView.BranchIndicator>
                <Show
                  when={nodeState().renaming}
                  fallback={
                    <TreeView.BranchText class={styles.BranchText}>
                      <Show when={nodeState().expanded} fallback={<FolderIcon />}>
                        <FolderOpenIcon />
                      </Show>
                      {props.node.name}
                    </TreeView.BranchText>
                  }
                >
                  <TreeView.NodeRenameInput class={styles.NodeRenameInput} />
                </Show>
              </TreeView.BranchControl>
              <TreeView.BranchContent class={styles.BranchContent}>
                <TreeView.BranchIndentGuide class={styles.BranchIndentGuide} />
                <For each={props.node.children}>
                  {(child, index) => <TreeNode node={child} indexPath={[...props.indexPath, index()]} />}
                </For>
              </TreeView.BranchContent>
            </TreeView.Branch>
          </Show>
        )}
      </TreeView.NodeContext>
    </TreeView.NodeProvider>
  )
}

interface Node {
  id: string
  name: string
  children?: Node[]
}

const initialCollection = 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' },
    ],
  },
})

```

#### Vue

```vue
<script setup lang="ts">
import { TreeView, createTreeCollection } from '@ark-ui/vue/tree-view'
import { shallowRef } from 'vue'
import RenameTreeNode from './rename-tree-node.vue'
import styles from 'styles/tree-view.module.css'

interface TreeNode {
  id: string
  name: string
  children?: TreeNode[]
}

const collection = shallowRef(
  createTreeCollection<TreeNode>({
    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' },
      ],
    },
  }),
)

const handleRenameComplete = (details: { indexPath: number[]; label: string }) => {
  const node = collection.value.at(details.indexPath)
  if (!node) return
  collection.value = collection.value.replace(details.indexPath, { ...node, name: details.label })
}
</script>

<template>
  <TreeView.Root
    :class="styles.Root"
    :collection="collection"
    :canRename="() => true"
    @rename-complete="handleRenameComplete"
  >
    <TreeView.Label :class="styles.Label">Tree (Press F2 to rename)</TreeView.Label>
    <TreeView.Tree :class="styles.Tree">
      <RenameTreeNode
        v-for="(node, index) in collection.rootNode.children"
        :key="node.id"
        :node="node"
        :indexPath="[index]"
      />
    </TreeView.Tree>
  </TreeView.Root>
</template>

```

#### Svelte

```svelte
<script lang="ts">
  import { TreeView, createTreeCollection } from '$lib'
  import { ChevronRightIcon, FileIcon, FolderIcon, FolderOpenIcon } from 'lucide-svelte'
  import styles from 'styles/tree-view.module.css'

  interface TreeNode {
    id: string
    name: string
    children?: TreeNode[]
  }

  let collection = $state(
    createTreeCollection<TreeNode>({
      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' },
        ],
      },
    }),
  )

  function handleRenameComplete(details: { indexPath: number[]; label: string }) {
    const node = collection.at(details.indexPath)
    if (!node) return
    collection = collection.replace(details.indexPath, { ...node, name: details.label })
  }
</script>

<TreeView.Root class={styles.Root} {collection} canRename={() => true} onRenameComplete={handleRenameComplete}>
  <TreeView.Label class={styles.Label}>Tree (Press F2 to rename)</TreeView.Label>
  <TreeView.Tree class={styles.Tree}>
    {#each collection.rootNode.children ?? [] as node, index (node.id)}
      {@render renderNode(node, [index])}
    {/each}
  </TreeView.Tree>
</TreeView.Root>

{#snippet renderNode(node: TreeNode, indexPath: number[])}
  <TreeView.NodeProvider {node} {indexPath}>
    <TreeView.NodeContext>
      {#snippet render(nodeState)}
        {#if node.children}
          <TreeView.Branch class={styles.Branch}>
            <TreeView.BranchControl class={styles.BranchControl}>
              <TreeView.BranchIndicator class={styles.BranchIndicator}>
                <ChevronRightIcon />
              </TreeView.BranchIndicator>
              {#if nodeState().renaming}
                <TreeView.NodeRenameInput class={styles.NodeRenameInput} />
              {:else}
                <TreeView.BranchText class={styles.BranchText}>
                  {#if nodeState().expanded}
                    <FolderOpenIcon />
                  {:else}
                    <FolderIcon />
                  {/if}
                  {node.name}
                </TreeView.BranchText>
              {/if}
            </TreeView.BranchControl>
            <TreeView.BranchContent class={styles.BranchContent}>
              <TreeView.BranchIndentGuide class={styles.BranchIndentGuide} />
              {#each node.children as child, index (child.id)}
                {@render renderNode(child, [...indexPath, index])}
              {/each}
            </TreeView.BranchContent>
          </TreeView.Branch>
        {:else}
          <TreeView.Item class={styles.Item}>
            <FileIcon />
            {#if nodeState().renaming}
              <TreeView.NodeRenameInput class={styles.NodeRenameInput} />
            {:else}
              <TreeView.ItemText class={styles.ItemText}>{node.name}</TreeView.ItemText>
            {/if}
          </TreeView.Item>
        {/if}
      {/snippet}
    </TreeView.NodeContext>
  </TreeView.NodeProvider>
{/snippet}

```

## Guides

### Type Safety

The `TreeView.RootComponent` type enables you to create typed wrapper components that maintain full type safety for tree
nodes.

```tsx
import { TreeView as ArkTreeView } from '@ark-ui/react/tree-view'

const TreeView: ArkTreeView.RootComponent = (props) => {
  return <ArkTreeView.Root {...props}>{/* ... */}</ArkTreeView.Root>
}
```

Use the wrapper with full type inference on `onSelectionChange` and other callbacks:

```tsx
const App = () => {
  const collection = createTreeCollection({
    initialItems: [
      { id: '1', label: 'React', children: [] },
      { id: '2', label: 'Vue', children: [] },
    ],
  })
  return (
    <TreeView
      collection={collection}
      onSelectionChange={(e) => {
        // e.items is typed as Array<{ id: string, label: string, children: [] }>
        console.log(e.items)
      }}
    >
      {/* ... */}
    </TreeView>
  )
}
```

## API Reference

### Props

**Component API Reference**

#### React

**Root Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `collection` | `TreeCollection<T>` | Yes | The collection of tree nodes |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `canRename` | `(node: T, indexPath: IndexPath) => boolean` | No | Function to determine if a node can be renamed |
| `checkedValue` | `string[]` | No | The controlled checked node value |
| `defaultCheckedValue` | `string[]` | No | The initial checked node value when rendered.
Use when you don't need to control the checked node value. |
| `defaultExpandedValue` | `string[]` | No | The initial expanded node ids when rendered.
Use when you don't need to control the expanded node value. |
| `defaultFocusedValue` | `string` | No | The initial focused node value when rendered.
Use when you don't need to control the focused node value. |
| `defaultSelectedValue` | `string[]` | No | The initial selected node value when rendered.
Use when you don't need to control the selected node value. |
| `expandedValue` | `string[]` | No | The controlled expanded node ids |
| `expandOnClick` | `boolean` | No | Whether clicking on a branch should open it or not |
| `focusedValue` | `string` | No | The value of the focused node |
| `ids` | `Partial<{ root: string; tree: string; label: string; node: (value: string) => string }>` | No | The ids of the tree elements. Useful for composition. |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `loadChildren` | `(details: LoadChildrenDetails<T>) => Promise<T[]>` | No | Function to load children for a node asynchronously.
When provided, branches will wait for this promise to resolve before expanding. |
| `onBeforeRename` | `(details: RenameCompleteDetails) => boolean` | No | Called before a rename is completed. Return false to prevent the rename. |
| `onCheckedChange` | `(details: CheckedChangeDetails) => void` | No | Called when the checked value changes |
| `onExpandedChange` | `(details: ExpandedChangeDetails<T>) => void` | No | Called when the tree is opened or closed |
| `onFocusChange` | `(details: FocusChangeDetails<T>) => void` | No | Called when the focused node changes |
| `onLoadChildrenComplete` | `(details: LoadChildrenCompleteDetails<T>) => void` | No | Called when a node finishes loading children |
| `onLoadChildrenError` | `(details: LoadChildrenErrorDetails<T>) => void` | No | Called when loading children fails for one or more nodes |
| `onRenameComplete` | `(details: RenameCompleteDetails) => void` | No | Called when a node label rename is completed |
| `onRenameStart` | `(details: RenameStartDetails<T>) => void` | No | Called when a node starts being renamed |
| `onSelectionChange` | `(details: SelectionChangeDetails<T>) => void` | No | Called when the selection changes |
| `scrollToIndexFn` | `(details: ScrollToIndexDetails<T>) => void` | No | Function to scroll to a specific index.
Useful for virtualized tree views. |
| `selectedValue` | `string[]` | No | The controlled selected node value |
| `selectionMode` | `'multiple' | 'single'` | No | Whether the tree supports multiple selection
- "single": only one node can be selected
- "multiple": multiple nodes can be selected |
| `typeahead` | `boolean` | No | Whether the tree supports typeahead search |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |


**BranchContent Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**BranchContent Data Attributes:**

| 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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**BranchControl Data Attributes:**

| 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-renaming]` |  |
| `[data-value]` | The value of the item |
| `[data-depth]` | The depth of the item |
| `[data-loading]` | Present when loading |


**BranchIndentGuide Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**BranchIndentGuide Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | tree-view |
| `[data-part]` | branch-indent-guide |
| `[data-depth]` | The depth of the item |


**BranchIndicator Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**BranchIndicator Data Attributes:**

| 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 |
| `[data-loading]` | Present when loading |


**Branch Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Branch Data Attributes:**

| 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 |
| `[data-loading]` | Present when loading |


**Branch CSS Variables:**

| Variable | Description |
|----------|-------------|
| `--depth` | The depth value for the Branch |


**BranchText Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**BranchText Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | tree-view |
| `[data-part]` | branch-text |
| `[data-disabled]` | Present when disabled |
| `[data-state]` | "open" | "closed" |
| `[data-loading]` | Present when loading |


**BranchTrigger Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**BranchTrigger Data Attributes:**

| 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 |
| `[data-loading]` | Present when loading |


**ItemIndicator Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**ItemIndicator Data Attributes:**

| 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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Item Data Attributes:**

| 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-renaming]` |  |
| `[data-depth]` | The depth of the item |


**Item CSS Variables:**

| Variable | Description |
|----------|-------------|
| `--depth` | The depth value for the Item |


**ItemText Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**ItemText Data Attributes:**

| 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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**NodeCheckboxIndicator Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `fallback` | `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ReactPortal | Promise<...>` | No |  |
| `indeterminate` | `string | number | bigint | boolean | ReactElement<unknown, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ReactPortal | Promise<...>` | No |  |


**NodeCheckbox Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**NodeCheckbox Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | tree-view |
| `[data-part]` | node-checkbox |
| `[data-state]` | "checked" | "unchecked" | "indeterminate" |
| `[data-disabled]` | Present when disabled |


**NodeProvider Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `indexPath` | `number[]` | Yes | The index path of the tree node |
| `node` | `NonNullable<T>` | No | The tree node |


**NodeRenameInput Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**RootProvider Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `value` | `UseTreeViewReturn<T>` | Yes |  |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |


**Tree Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


#### Solid

**Root Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `collection` | `TreeCollection<T>` | Yes | The collection of tree nodes |
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `canRename` | `(node: any, indexPath: IndexPath) => boolean` | No | Function to determine if a node can be renamed |
| `checkedValue` | `string[]` | No | The controlled checked node value |
| `defaultCheckedValue` | `string[]` | No | The initial checked node value when rendered.
Use when you don't need to control the checked node value. |
| `defaultExpandedValue` | `string[]` | No | The initial expanded node ids when rendered.
Use when you don't need to control the expanded node value. |
| `defaultFocusedValue` | `string` | No | The initial focused node value when rendered.
Use when you don't need to control the focused node value. |
| `defaultSelectedValue` | `string[]` | No | The initial selected node value when rendered.
Use when you don't need to control the selected node value. |
| `expandedValue` | `string[]` | No | The controlled expanded node ids |
| `expandOnClick` | `boolean` | No | Whether clicking on a branch should open it or not |
| `focusedValue` | `string` | No | The value of the focused node |
| `id` | `string` | No | The unique identifier of the machine. |
| `ids` | `Partial<{ root: string; tree: string; label: string; node: (value: string) => string }>` | No | The ids of the tree elements. Useful for composition. |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `loadChildren` | `(details: LoadChildrenDetails<any>) => Promise<any[]>` | No | Function to load children for a node asynchronously.
When provided, branches will wait for this promise to resolve before expanding. |
| `onBeforeRename` | `(details: RenameCompleteDetails) => boolean` | No | Called before a rename is completed. Return false to prevent the rename. |
| `onCheckedChange` | `(details: CheckedChangeDetails) => void` | No | Called when the checked value changes |
| `onExpandedChange` | `(details: ExpandedChangeDetails<any>) => void` | No | Called when the tree is opened or closed |
| `onFocusChange` | `(details: FocusChangeDetails<any>) => void` | No | Called when the focused node changes |
| `onLoadChildrenComplete` | `(details: LoadChildrenCompleteDetails<any>) => void` | No | Called when a node finishes loading children |
| `onLoadChildrenError` | `(details: LoadChildrenErrorDetails<any>) => void` | No | Called when loading children fails for one or more nodes |
| `onRenameComplete` | `(details: RenameCompleteDetails) => void` | No | Called when a node label rename is completed |
| `onRenameStart` | `(details: RenameStartDetails<any>) => void` | No | Called when a node starts being renamed |
| `onSelectionChange` | `(details: SelectionChangeDetails<any>) => void` | No | Called when the selection changes |
| `scrollToIndexFn` | `(details: ScrollToIndexDetails<any>) => void` | No | Function to scroll to a specific index.
Useful for virtualized tree views. |
| `selectedValue` | `string[]` | No | The controlled selected node value |
| `selectionMode` | `'multiple' | 'single'` | No | Whether the tree supports multiple selection
- "single": only one node can be selected
- "multiple": multiple nodes can be selected |
| `typeahead` | `boolean` | No | Whether the tree supports typeahead search |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |


**BranchContent Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**BranchContent Data Attributes:**

| 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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**BranchControl Data Attributes:**

| 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-renaming]` |  |
| `[data-value]` | The value of the item |
| `[data-depth]` | The depth of the item |
| `[data-loading]` | Present when loading |


**BranchIndentGuide Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**BranchIndentGuide Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | tree-view |
| `[data-part]` | branch-indent-guide |
| `[data-depth]` | The depth of the item |


**BranchIndicator Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**BranchIndicator Data Attributes:**

| 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 |
| `[data-loading]` | Present when loading |


**Branch Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Branch Data Attributes:**

| 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 |
| `[data-loading]` | Present when loading |


**Branch CSS Variables:**

| Variable | Description |
|----------|-------------|
| `--depth` | The depth value for the Branch |


**BranchText Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'span'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**BranchText Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | tree-view |
| `[data-part]` | branch-text |
| `[data-disabled]` | Present when disabled |
| `[data-state]` | "open" | "closed" |
| `[data-loading]` | Present when loading |


**BranchTrigger Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**BranchTrigger Data Attributes:**

| 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 |
| `[data-loading]` | Present when loading |


**ItemIndicator Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**ItemIndicator Data Attributes:**

| 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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Item Data Attributes:**

| 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-renaming]` |  |
| `[data-depth]` | The depth of the item |


**Item CSS Variables:**

| Variable | Description |
|----------|-------------|
| `--depth` | The depth value for the Item |


**ItemText Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'span'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**ItemText Data Attributes:**

| 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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'h3'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**NodeCheckboxIndicator Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `fallback` | `number | boolean | Node | (string & {}) | ArrayElement` | No |  |
| `indeterminate` | `number | boolean | Node | (string & {}) | ArrayElement` | No |  |


**NodeCheckbox Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'span'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**NodeCheckbox Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | tree-view |
| `[data-part]` | node-checkbox |
| `[data-state]` | "checked" | "unchecked" | "indeterminate" |
| `[data-disabled]` | Present when disabled |


**NodeProvider Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `indexPath` | `number[]` | Yes | The index path of the tree node |
| `node` | `NonNullable<T>` | No | The tree node |


**NodeRenameInput Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'input'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**RootProvider Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `value` | `UseTreeViewReturn<T>` | Yes |  |
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |


**Tree Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `(props: ParentProps<'div'>) => Element` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


#### Vue

**Root Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `collection` | `TreeCollection<T>` | Yes | The collection of tree nodes |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `canRename` | `(node: T, indexPath: number[]) => boolean` | No | Function to determine if a node can be renamed |
| `checkedValue` | `string[]` | No | The controlled checked node values |
| `defaultCheckedValue` | `string[]` | No | The initial checked node values when rendered.
Use when you don't need to control the checked node values. |
| `defaultExpandedValue` | `string[]` | No | The initial expanded node values when rendered.
Use when you don't need to control the expanded node values. |
| `defaultFocusedValue` | `string` | No | The initial focused node value when rendered.
Use when you don't need to control the focused node value. |
| `defaultSelectedValue` | `string[]` | No | The initial selected node values when rendered.
Use when you don't need to control the selected node values. |
| `expandedValue` | `string[]` | No | The controlled expanded node values |
| `expandOnClick` | `boolean` | No | Whether clicking on a branch should open it or not |
| `focusedValue` | `string` | No | The id of the focused node |
| `id` | `string` | No | The unique identifier of the machine. |
| `ids` | `Partial<{ root: string; tree: string; label: string; node(value: string): string }>` | No | The ids of the tree elements. Useful for composition. |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `loadChildren` | `(details: LoadChildrenDetails<T>) => Promise<T[]>` | No | A function that loads the children of a node. |
| `selectedValue` | `string[]` | No | The controlled selected node values |
| `selectionMode` | `'single' | 'multiple'` | No | Whether the tree supports multiple selection
- "single": only one node can be selected
- "multiple": multiple nodes can be selected |
| `typeahead` | `boolean` | No | Whether the tree supports typeahead search |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |


**BranchContent Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**BranchContent Data Attributes:**

| 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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**BranchControl Data Attributes:**

| 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-renaming]` |  |
| `[data-value]` | The value of the item |
| `[data-depth]` | The depth of the item |
| `[data-loading]` | Present when loading |


**BranchIndentGuide Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**BranchIndentGuide Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | tree-view |
| `[data-part]` | branch-indent-guide |
| `[data-depth]` | The depth of the item |


**BranchIndicator Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**BranchIndicator Data Attributes:**

| 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 |
| `[data-loading]` | Present when loading |


**Branch Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Branch Data Attributes:**

| 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 |
| `[data-loading]` | Present when loading |


**Branch CSS Variables:**

| Variable | Description |
|----------|-------------|
| `--depth` | The depth value for the Branch |


**BranchText Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**BranchText Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | tree-view |
| `[data-part]` | branch-text |
| `[data-disabled]` | Present when disabled |
| `[data-state]` | "open" | "closed" |
| `[data-loading]` | Present when loading |


**BranchTrigger Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**BranchTrigger Data Attributes:**

| 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 |
| `[data-loading]` | Present when loading |


**ItemIndicator Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**ItemIndicator Data Attributes:**

| 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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**Item Data Attributes:**

| 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-renaming]` |  |
| `[data-depth]` | The depth of the item |


**Item CSS Variables:**

| Variable | Description |
|----------|-------------|
| `--depth` | The depth value for the Item |


**ItemText Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**ItemText Data Attributes:**

| 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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**NodeCheckboxIndicator Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `fallback` | `{}` | No |  |
| `indeterminate` | `{}` | No |  |


**NodeCheckbox Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**NodeCheckbox Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | tree-view |
| `[data-part]` | node-checkbox |
| `[data-state]` | "checked" | "unchecked" | "indeterminate" |
| `[data-disabled]` | Present when disabled |


**NodeProvider Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `indexPath` | `number[]` | Yes | The index path of the tree node |
| `node` | `NonNullable<T>` | No | The tree node |


**NodeRenameInput Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


**RootProvider Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `value` | `TreeViewApi<PropTypes, T>` | Yes |  |
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |


**Tree Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `boolean` | No | Use the provided child element as the default rendered element, combining their props and behavior. |


#### Svelte

**Root Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'div'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `canRename` | `(node: T, indexPath: IndexPath) => boolean` | No | Function to determine if a node can be renamed |
| `checkedValue` | `string[]` | No | The controlled checked node value |
| `collection` | `TreeCollection<T>` | No | The tree collection data |
| `defaultCheckedValue` | `string[]` | No | The initial checked node value when rendered.
Use when you don't need to control the checked node value. |
| `defaultExpandedValue` | `string[]` | No | The initial expanded node ids when rendered.
Use when you don't need to control the expanded node value. |
| `defaultFocusedValue` | `string` | No | The initial focused node value when rendered.
Use when you don't need to control the focused node value. |
| `defaultSelectedValue` | `string[]` | No | The initial selected node value when rendered.
Use when you don't need to control the selected node value. |
| `expandedValue` | `string[]` | No | The controlled expanded node ids |
| `expandOnClick` | `boolean` | No | Whether clicking on a branch should open it or not |
| `focusedValue` | `string` | No | The value of the focused node |
| `id` | `string` | No | The unique identifier of the machine. |
| `ids` | `Partial<{ root: string; tree: string; label: string; node: (value: string) => string }>` | No | The ids of the tree elements. Useful for composition. |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `loadChildren` | `(details: LoadChildrenDetails<T>) => Promise<T[]>` | No | Function to load children for a node asynchronously.
When provided, branches will wait for this promise to resolve before expanding. |
| `onBeforeRename` | `(details: RenameCompleteDetails) => boolean` | No | Called before a rename is completed. Return false to prevent the rename. |
| `onCheckedChange` | `(details: CheckedChangeDetails) => void` | No | Called when the checked value changes |
| `onExpandedChange` | `(details: ExpandedChangeDetails<T>) => void` | No | Called when the tree is opened or closed |
| `onFocusChange` | `(details: FocusChangeDetails<T>) => void` | No | Called when the focused node changes |
| `onLoadChildrenComplete` | `(details: LoadChildrenCompleteDetails<T>) => void` | No | Called when a node finishes loading children |
| `onLoadChildrenError` | `(details: LoadChildrenErrorDetails<T>) => void` | No | Called when loading children fails for one or more nodes |
| `onRenameComplete` | `(details: RenameCompleteDetails) => void` | No | Called when a node label rename is completed |
| `onRenameStart` | `(details: RenameStartDetails<T>) => void` | No | Called when a node starts being renamed |
| `onSelectionChange` | `(details: SelectionChangeDetails<T>) => void` | No | Called when the selection changes |
| `ref` | `Element` | No |  |
| `scrollToIndexFn` | `(details: ScrollToIndexDetails<T>) => void` | No | Function to scroll to a specific index.
Useful for virtualized tree views. |
| `selectedValue` | `string[]` | No | The controlled selected node value |
| `selectionMode` | `'multiple' | 'single'` | No | Whether the tree supports multiple selection
- "single": only one node can be selected
- "multiple": multiple nodes can be selected |
| `typeahead` | `boolean` | No | Whether the tree supports typeahead search |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |


**BranchContent Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'ul'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**BranchContent Data Attributes:**

| 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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'div'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**BranchControl Data Attributes:**

| 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-renaming]` |  |
| `[data-value]` | The value of the item |
| `[data-depth]` | The depth of the item |
| `[data-loading]` | Present when loading |


**BranchIndentGuide Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'div'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**BranchIndentGuide Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | tree-view |
| `[data-part]` | branch-indent-guide |
| `[data-depth]` | The depth of the item |


**BranchIndicator Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'div'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**BranchIndicator Data Attributes:**

| 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 |
| `[data-loading]` | Present when loading |


**Branch Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'li'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**Branch Data Attributes:**

| 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 |
| `[data-loading]` | Present when loading |


**Branch CSS Variables:**

| Variable | Description |
|----------|-------------|
| `--depth` | The depth value for the Branch |


**BranchText Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'span'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**BranchText Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | tree-view |
| `[data-part]` | branch-text |
| `[data-disabled]` | Present when disabled |
| `[data-state]` | "open" | "closed" |
| `[data-loading]` | Present when loading |


**BranchTrigger Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'button'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**BranchTrigger Data Attributes:**

| 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 |
| `[data-loading]` | Present when loading |


**Context Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `render` | `Snippet<[UseTreeViewContext<any>]>` | Yes |  |


**ItemIndicator Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'div'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**ItemIndicator Data Attributes:**

| 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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'li'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**Item Data Attributes:**

| 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-renaming]` |  |
| `[data-depth]` | The depth of the item |


**Item CSS Variables:**

| Variable | Description |
|----------|-------------|
| `--depth` | The depth value for the Item |


**ItemText Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'span'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**ItemText Data Attributes:**

| 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 Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'h3'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**NodeCheckboxIndicator Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `fallback` | `Snippet<[]>` | No |  |
| `indeterminate` | `Snippet<[]>` | No |  |


**NodeCheckbox Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'span'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**NodeCheckbox Data Attributes:**

| Attribute | Value |
|-----------|-------|
| `[data-scope]` | tree-view |
| `[data-part]` | node-checkbox |
| `[data-state]` | "checked" | "unchecked" | "indeterminate" |
| `[data-disabled]` | Present when disabled |


**NodeContext Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `render` | `Snippet<[UseTreeViewNodeContext]>` | Yes |  |


**NodeProvider Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `indexPath` | `number[]` | Yes |  |
| `node` | `NonNullable<T>` | No |  |


**NodeRenameInput Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'input'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


**RootProvider Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `value` | `UseTreeViewReturn<T>` | Yes |  |
| `asChild` | `Snippet<[PropsFn<'div'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `lazyMount` | `boolean` | No | Whether to enable lazy mounting |
| `ref` | `Element` | No |  |
| `unmountOnExit` | `boolean` | No | Whether to unmount on exit. |


**Tree Props:**

| Prop | Type | Required | Description |
|------|------|----------|-------------|
| `asChild` | `Snippet<[PropsFn<'ul'>]>` | No | Use the provided child element as the default rendered element, combining their props and behavior. |
| `ref` | `Element` | No |  |


### Context

**API:**

| Property | Type | Description |
|----------|------|-------------|
| `collection` | `TreeCollection<V>` | The tree collection data |
| `expandedValue` | `string[]` | The value of the expanded nodes. |
| `setExpandedValue` | `(value: string[]) => void` | Sets the expanded value |
| `selectedValue` | `string[]` | The value of the selected nodes. |
| `setSelectedValue` | `(value: string[]) => void` | Sets the selected value |
| `checkedValue` | `string[]` | The value of the checked nodes |
| `toggleChecked` | `(value: string, isBranch: boolean) => void` | Toggles the checked value of a node |
| `setChecked` | `(value: string[]) => void` | Sets the checked value of a node |
| `clearChecked` | `VoidFunction` | Clears the checked value of a node |
| `getCheckedMap` | `() => CheckedValueMap` | Returns the checked details of branch and leaf nodes |
| `getVisibleNodes` | `() => VisibleNode<V>[]` | Returns the visible nodes as a flat array of nodes and their index path.
Useful for rendering virtualized tree views. |
| `expand` | `(value?: string[]) => void` | Function to expand nodes.
If no value is provided, all nodes will be expanded |
| `collapse` | `(value?: string[]) => void` | Function to collapse nodes
If no value is provided, all nodes will be collapsed |
| `select` | `(value?: string[]) => void` | Function to select nodes
If no value is provided, all nodes will be selected |
| `deselect` | `(value?: string[]) => void` | Function to deselect nodes
If no value is provided, all nodes will be deselected |
| `focus` | `(value: string) => void` | Function to focus a node by value |
| `selectParent` | `(value: string) => void` | Function to select the parent node of the focused node |
| `expandParent` | `(value: string) => void` | Function to expand the parent node of the focused node |
| `startRenaming` | `(value: string) => void` | Function to start renaming a node by value |
| `submitRenaming` | `(value: string, label: string) => void` | Function to submit the rename and update the node label |
| `cancelRenaming` | `() => void` | Function to cancel renaming without changes |


## Accessibility

Complies with the [Tree View WAI-ARIA design pattern](https://www.w3.org/WAI/ARIA/apg/patterns/treeview/).

### Keyboard Support

<KeyBindingsTable id="tree-view" />