---
title: "Tree"
description: "A tree view component to display and interact with hierarchical data structures."
canonical_url: "https://vinicunca.dev/pohon/components/tree"
---
# Tree

> A tree view component to display and interact with hierarchical data structures.

## Usage

Use the Tree component to display a hierarchical structure of items.

```vue
<script setup lang="ts">
import type { TreeItem } from 'pohon-ui'

const items = ref<TreeItem[]>([
  {
    label: "app/",
    defaultExpanded: true,
    children: [
      {
        label: "composables/",
        children: [
          {
            label: "useAuth.ts",
            icon: "i-vscode-icons-file-type-typescript"
          },
          {
            label: "useUser.ts",
            icon: "i-vscode-icons-file-type-typescript"
          }
        ]
      },
      {
        label: "components/",
        defaultExpanded: true,
        children: [
          {
            label: "Card.vue",
            icon: "i-vscode-icons-file-type-vue"
          },
          {
            label: "Button.vue",
            icon: "i-vscode-icons-file-type-vue"
          }
        ]
      }
    ]
  },
  {
    label: "app.vue",
    icon: "i-vscode-icons-file-type-vue"
  },
  {
    label: "nuxt.config.ts",
    icon: "i-vscode-icons-file-type-nuxt"
  }
])
</script>

<template>
  <PTree :items="items" />
</template>
```

### Items

Use the `items` prop as an array of objects with the following properties:

- `icon?: string`
- `label?: string`
- `trailingIcon?: string`
- `defaultExpanded?: boolean`
- `disabled?: boolean`
- `slot?: string`
- `children?: TreeItem[]`
- `onToggle?: (e: TreeItemToggleEvent<TreeItem>) => void`
- `onSelect?: (e: TreeItemSelectEvent<TreeItem>) => void`
- `class?: any`
- `ui?: { item?: ClassNameValue, itemWithChildren?: ClassNameValue, link?: ClassNameValue, linkLeadingIcon?: ClassNameValue, linkLabel?: ClassNameValue, linkTrailing?: ClassNameValue, linkTrailingIcon?: ClassNameValue, listWithChildren?: ClassNameValue }`

> [!NOTE]
> 
> A unique identifier is required for each item. The component will use the `label` prop as identifier if no `get-key` is provided. Ideally you should provide a `get-key` function prop to return a unique identifier. Alternatively, you can use the `labelKey` prop to specify which property to use as the unique identifier.

```vue
<script setup lang="ts">
import type { TreeItem } from 'pohon-ui'

const items = ref<TreeItem[]>([
  {
    label: "app/",
    defaultExpanded: true,
    children: [
      {
        label: "composables/",
        children: [
          {
            label: "useAuth.ts",
            icon: "i-vscode-icons-file-type-typescript"
          },
          {
            label: "useUser.ts",
            icon: "i-vscode-icons-file-type-typescript"
          }
        ]
      },
      {
        label: "components/",
        defaultExpanded: true,
        children: [
          {
            label: "Card.vue",
            icon: "i-vscode-icons-file-type-vue"
          },
          {
            label: "Button.vue",
            icon: "i-vscode-icons-file-type-vue"
          }
        ]
      }
    ]
  },
  {
    label: "app.vue",
    icon: "i-vscode-icons-file-type-vue"
  },
  {
    label: "nuxt.config.ts",
    icon: "i-vscode-icons-file-type-nuxt"
  }
])
</script>

<template>
  <PTree :items="items" />
</template>
```

### Multiple

Use the `multiple` prop to allow multiple item selections.

```vue
<script setup lang="ts">
import type { TreeItem } from 'pohon-ui'

const items = ref<TreeItem[]>([
  {
    label: "app/",
    defaultExpanded: true,
    children: [
      {
        label: "composables/",
        children: [
          {
            label: "useAuth.ts",
            icon: "i-vscode-icons-file-type-typescript"
          },
          {
            label: "useUser.ts",
            icon: "i-vscode-icons-file-type-typescript"
          }
        ]
      },
      {
        label: "components/",
        defaultExpanded: true,
        children: [
          {
            label: "Card.vue",
            icon: "i-vscode-icons-file-type-vue"
          },
          {
            label: "Button.vue",
            icon: "i-vscode-icons-file-type-vue"
          }
        ]
      }
    ]
  },
  {
    label: "app.vue",
    icon: "i-vscode-icons-file-type-vue"
  },
  {
    label: "nuxt.config.ts",
    icon: "i-vscode-icons-file-type-nuxt"
  }
])
</script>

<template>
  <PTree multiple :items="items" />
</template>
```

### Nested

Use the `nested` prop to control whether the Tree is rendered with nested structure or as a flat list. Defaults to `true`.

```vue
<script setup lang="ts">
import type { TreeItem } from 'pohon-ui'

const items = ref<TreeItem[]>([
  {
    label: "app/",
    defaultExpanded: true,
    children: [
      {
        label: "composables/",
        children: [
          {
            label: "useAuth.ts",
            icon: "i-vscode-icons-file-type-typescript"
          },
          {
            label: "useUser.ts",
            icon: "i-vscode-icons-file-type-typescript"
          }
        ]
      },
      {
        label: "components/",
        defaultExpanded: true,
        children: [
          {
            label: "Card.vue",
            icon: "i-vscode-icons-file-type-vue"
          },
          {
            label: "Button.vue",
            icon: "i-vscode-icons-file-type-vue"
          }
        ]
      }
    ]
  },
  {
    label: "app.vue",
    icon: "i-vscode-icons-file-type-vue"
  },
  {
    label: "nuxt.config.ts",
    icon: "i-vscode-icons-file-type-nuxt"
  }
])
</script>

<template>
  <PTree :nested="false" :items="items" />
</template>
```

> [!NOTE]
> See: #with-virtualization
> 
> When `nested` is `false`, all items are rendered at the same level with indentation to indicate hierarchy. This is useful for virtualization or drag and drop functionality.

### Color

Use the `color` prop to change the color of the Tree.

```vue
<script setup lang="ts">
import type { TreeItem } from 'pohon-ui'

const items = ref<TreeItem[]>([
  {
    label: "app/",
    defaultExpanded: true,
    children: [
      {
        label: "composables/",
        children: [
          {
            label: "useAuth.ts",
            icon: "i-vscode-icons-file-type-typescript"
          },
          {
            label: "useUser.ts",
            icon: "i-vscode-icons-file-type-typescript"
          }
        ]
      },
      {
        label: "components/",
        defaultExpanded: true,
        children: [
          {
            label: "Card.vue",
            icon: "i-vscode-icons-file-type-vue"
          },
          {
            label: "Button.vue",
            icon: "i-vscode-icons-file-type-vue"
          }
        ]
      }
    ]
  },
  {
    label: "app.vue",
    icon: "i-vscode-icons-file-type-vue"
  },
  {
    label: "nuxt.config.ts",
    icon: "i-vscode-icons-file-type-nuxt"
  }
])
</script>

<template>
  <PTree color="neutral" :items="items" />
</template>
```

### Size

Use the `size` prop to change the size of the Tree.

```vue
<script setup lang="ts">
import type { TreeItem } from 'pohon-ui'

const items = ref<TreeItem[]>([
  {
    label: "app/",
    defaultExpanded: true,
    children: [
      {
        label: "composables/",
        children: [
          {
            label: "useAuth.ts",
            icon: "i-vscode-icons-file-type-typescript"
          },
          {
            label: "useUser.ts",
            icon: "i-vscode-icons-file-type-typescript"
          }
        ]
      },
      {
        label: "components/",
        defaultExpanded: true,
        children: [
          {
            label: "Card.vue",
            icon: "i-vscode-icons-file-type-vue"
          },
          {
            label: "Button.vue",
            icon: "i-vscode-icons-file-type-vue"
          }
        ]
      }
    ]
  },
  {
    label: "app.vue",
    icon: "i-vscode-icons-file-type-vue"
  },
  {
    label: "nuxt.config.ts",
    icon: "i-vscode-icons-file-type-nuxt"
  }
])
</script>

<template>
  <PTree size="xl" :items="items" />
</template>
```

### Trailing Icon

Use the `trailing-icon` prop to customize the trailing [Icon](https://vinicunca.dev/pohon/components/icon) of a parent node. Defaults to `i-lucide-chevron-down`.

> [!NOTE]
> 
> If an icon is specified for an item, it will always take precedence over these props.

```vue
<script setup lang="ts">
import type { TreeItem } from 'pohon-ui'

const items = ref<TreeItem[]>([
  {
    label: "app/",
    defaultExpanded: true,
    children: [
      {
        label: "composables/",
        trailingIcon: "i-lucide-chevron-down",
        children: [
          {
            label: "useAuth.ts",
            icon: "i-vscode-icons-file-type-typescript"
          },
          {
            label: "useUser.ts",
            icon: "i-vscode-icons-file-type-typescript"
          }
        ]
      },
      {
        label: "components/",
        defaultExpanded: true,
        children: [
          {
            label: "Card.vue",
            icon: "i-vscode-icons-file-type-vue"
          },
          {
            label: "Button.vue",
            icon: "i-vscode-icons-file-type-vue"
          }
        ]
      }
    ]
  },
  {
    label: "app.vue",
    icon: "i-vscode-icons-file-type-vue"
  },
  {
    label: "nuxt.config.ts",
    icon: "i-vscode-icons-file-type-nuxt"
  }
])
</script>

<template>
  <PTree trailing-icon="i-lucide-arrow-down" :items="items" />
</template>
```

**Nuxt:**

> [!TIP]
> See: /pohon/getting-started/integrations/icons/nuxt#theme
> 
> You can customize this icon globally in your `app.config.ts` under `ui.icons.chevronDown` key.

**Vue:**

> [!TIP]
> See: /pohon/getting-started/integrations/icons/vue#theme
> 
> You can customize this icon globally in your `vite.config.ts` under `ui.icons.chevronDown` key.

### Expanded Icon

Use the `expanded-icon` and `collapsed-icon` props to customize the icons of a parent node when it is expanded or collapsed. Defaults to `i-lucide-folder-open` and `i-lucide-folder` respectively.

```vue
<script setup lang="ts">
import type { TreeItem } from 'pohon-ui'

const items = ref<TreeItem[]>([
  {
    label: "app/",
    defaultExpanded: true,
    children: [
      {
        label: "composables/",
        children: [
          {
            label: "useAuth.ts",
            icon: "i-vscode-icons-file-type-typescript"
          },
          {
            label: "useUser.ts",
            icon: "i-vscode-icons-file-type-typescript"
          }
        ]
      },
      {
        label: "components/",
        defaultExpanded: true,
        children: [
          {
            label: "Card.vue",
            icon: "i-vscode-icons-file-type-vue"
          },
          {
            label: "Button.vue",
            icon: "i-vscode-icons-file-type-vue"
          }
        ]
      }
    ]
  },
  {
    label: "app.vue",
    icon: "i-vscode-icons-file-type-vue"
  },
  {
    label: "nuxt.config.ts",
    icon: "i-vscode-icons-file-type-nuxt"
  }
])
</script>

<template>
  <PTree expanded-icon="i-lucide-book-open" collapsed-icon="i-lucide-book" :items="items" />
</template>
```

**Nuxt:**

> [!TIP]
> See: /pohon/getting-started/integrations/icons/nuxt#theme
> 
> You can customize these icons globally in your `app.config.ts` under `ui.icons.folder` and `ui.icons.folderOpen` keys.

**Vue:**

> [!TIP]
> See: /pohon/getting-started/integrations/icons/vue#theme
> 
> You can customize these icons globally in your `vite.config.ts` under `ui.icons.folder` and `ui.icons.folderOpen` keys.

### Disabled

Use the `disabled` prop to prevent any user interaction with the Tree.

```vue
<script setup lang="ts">
import type { TreeItem } from 'pohon-ui'

const items = ref<TreeItem[]>([
  {
    label: "app",
    icon: "i-lucide-folder",
    defaultExpanded: true,
    children: [
      {
        label: "composables",
        icon: "i-lucide-folder",
        children: [
          {
            label: "useAuth.ts",
            icon: "i-vscode-icons-file-type-typescript"
          },
          {
            label: "useUser.ts",
            icon: "i-vscode-icons-file-type-typescript"
          }
        ]
      },
      {
        label: "components",
        icon: "i-lucide-folder",
        children: [
          {
            label: "Home",
            icon: "i-lucide-folder",
            children: [
              {
                label: "Card.vue",
                icon: "i-vscode-icons-file-type-vue"
              },
              {
                label: "Button.vue",
                icon: "i-vscode-icons-file-type-vue"
              }
            ]
          }
        ]
      }
    ]
  },
  {
    label: "app.vue",
    icon: "i-vscode-icons-file-type-vue"
  },
  {
    label: "nuxt.config.ts",
    icon: "i-vscode-icons-file-type-nuxt"
  }
])
</script>

<template>
  <PTree disabled :items="items" />
</template>
```

> [!NOTE]
> 
> You can also disable individual items using `item.disabled`.

## Examples

### Control selected item(s)

You can control the selected item(s) by using the `default-value` prop or the `v-model` directive.

```vue [TreeModelValueExample.vue]
<script setup lang="ts">
import type { TreeItem } from 'pohon-ui';

const items: Array<TreeItem> = [
  {
    label: 'app/',
    defaultExpanded: true,
    children: [
      {
        label: 'composables/',
        children: [
          { label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
          { label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' },
        ],
      },
      {
        label: 'components/',
        defaultExpanded: true,
        children: [
          { label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
          { label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' },
        ],
      },
    ],
  },
  { label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
  { label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' },
];

const value = ref();
</script>

<template>
  <PTree
    v-model="value"
    :items="items"
  />
</template>
```

> [!TIP]
> 
> Use the `get-key` prop to change the function used to get the unique key from each item when a `v-model` or `default-value` is provided.

If you want to prevent an item from being selected, you can use the `item.onSelect()` property or the global `select` event:

```vue [TreeOnSelectExample.vue]
<script setup lang="ts">
import type { TreeItemSelectEvent } from 'akar';
import type { TreeItem } from 'pohon-ui';

const items: Array<TreeItem> = [
  {
    label: 'app/',
    defaultExpanded: true,
    onSelect: (e: Event) => {
      e.preventDefault();
    },
    children: [
      {
        label: 'composables/',
        children: [
          { label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
          { label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' },
        ],
      },
      {
        label: 'components/',
        defaultExpanded: true,
        children: [
          { label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
          { label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' },
        ],
      },
    ],
  },
  { label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
  { label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' },
];

function onSelect(e: TreeItemSelectEvent<TreeItem>) {
  if (e.detail.originalEvent.type === 'click') {
    e.preventDefault();
  }
}
</script>

<template>
  <PTree
    :items="items"
    @select="onSelect"
  />
</template>
```

> [!NOTE]
> 
> This lets you expand or collapse a parent item without selecting it.

### Control expanded items

You can control the expanded items by using the `default-expanded` prop or the `v-model` directive.

```vue [TreeExpandedExample.vue]
<script setup lang="ts">
import type { TreeItem } from 'pohon-ui';

const items = [
  {
    label: 'app/',
    id: 'app',
    children: [
      {
        label: 'composables/',
        id: 'app/composables',
        children: [
          { label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
          { label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' },
        ],
      },
      {
        label: 'components/',
        id: 'app/components',
        children: [
          { label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
          { label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' },
        ],
      },
    ],
  },
  { label: 'app.vue', id: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
  { label: 'nuxt.config.ts', id: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' },
] satisfies Array<TreeItem>;

const expanded = ref(['app', 'app/composables']);
</script>

<template>
  <PTree
    v-model:expanded="expanded"
    :items="items"
    :get-key="i => i.id"
  />
</template>
```

If you want to prevent an item from being expanded, you can use the `item.onToggle()` property or the global `toggle` event:

```vue [TreeOnToggleExample.vue]
<script setup lang="ts">
import type { TreeItemToggleEvent } from 'akar';
import type { TreeItem } from 'pohon-ui';

const items: Array<TreeItem> = [
  {
    label: 'app/',
    defaultExpanded: true,
    onToggle: (e: Event) => {
      e.preventDefault();
    },
    children: [
      {
        label: 'composables/',
        children: [
          { label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
          { label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' },
        ],
      },
      {
        label: 'components/',
        defaultExpanded: true,
        children: [
          { label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
          { label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' },
        ],
      },
    ],
  },
  { label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
  { label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' },
];

function onToggle(e: TreeItemToggleEvent<TreeItem>) {
  if (e.detail.originalEvent.type === 'keydown') {
    e.preventDefault();
  }
}
</script>

<template>
  <PTree
    :items="items"
    @toggle="onToggle"
  />
</template>
```

> [!NOTE]
> 
> This lets you select a parent item without expanding or collapsing its children.

### With checkbox in items

You can use the `item-leading` slot to add a [Checkbox](https://vinicunca.dev/pohon/components/checkbox) to the items. Use the `multiple`, `propagate-select` and `bubble-select` props to enable multi-selection with parent-child relationship and the `select` and `toggle` events to control the selected and expanded state of the items.

```vue [TreeCheckboxItemsExample.vue]
<script setup lang="ts">
import type { TreeItemSelectEvent } from 'akar';
import type { TreeItem } from 'pohon-ui';

const items: Array<TreeItem> = [
  {
    label: 'app/',
    defaultExpanded: true,
    children: [
      {
        label: 'composables/',
        children: [
          { label: 'useAuth.ts' },
          { label: 'useUser.ts' },
        ],
      },
      {
        label: 'components/',
        defaultExpanded: true,
        children: [
          { label: 'Card.vue' },
          { label: 'Button.vue' },
        ],
      },
    ],
  },
  { label: 'app.vue' },
  { label: 'nuxt.config.ts' },
];

const value = ref<(typeof items)>([]);

function onSelect(e: TreeItemSelectEvent<TreeItem>) {
  if (e.detail.originalEvent.type === 'click') {
    e.preventDefault();
  }
}
</script>

<template>
  <PTree
    v-model="value"
    :as="{ link: 'div' }"
    :items="items"
    multiple
    propagate-select
    bubble-select
    @select="onSelect"
  >
    <template #item-leading="{ selected, indeterminate, handleSelect }">
      <PCheckbox
        :model-value="indeterminate ? 'indeterminate' : selected"
        tabindex="-1"
        @change="handleSelect"
        @click.stop
      />
    </template>
  </PTree>
</template>
```

> [!NOTE]
> 
> This example uses the `as` prop to change the items from `button` to `div` as the [`Checkbox`](https://vinicunca.dev/pohon/components/checkbox) is also rendered as a `button`.

### With drag and drop

Use the [`useSortable`](https://vueuse.org/integrations/useSortable/) composable from [`@vueuse/integrations`](https://vueuse.org/integrations/README.html) to enable drag and drop functionality on the Tree. This integration wraps [Sortable.js](https://sortablejs.github.io/Sortable/) to provide a seamless drag and drop experience.

```vue [TreeDragAndDropExample.vue]
<script setup lang="ts">
import type { TreeItem } from 'pohon-ui';
import { useSortable } from '@vueuse/integrations/useSortable';

const items = shallowRef<Array<TreeItem>>([
  {
    label: 'app/',
    defaultExpanded: true,
    children: [
      {
        label: 'composables/',
        children: [
          { label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
          { label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' },
        ],
      },
      {
        label: 'components/',
        defaultExpanded: true,
        children: [
          { label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
          { label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' },
        ],
      },
    ],
  },
  { label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
  { label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' },
]);

function flatten(items: Array<TreeItem>, parent = items): Array<{ item: TreeItem; parent: Array<TreeItem>; index: number }> {
  return items.flatMap((item, index) => [
    { item, parent, index },
    ...(item.children?.length && item.defaultExpanded ? flatten(item.children, item.children) : []),
  ]);
}

function moveItem(oldIndex: number, newIndex: number) {
  if (oldIndex === newIndex) {
    return;
  }

  const flat = flatten(items.value);
  const source = flat[oldIndex];
  const target = flat[newIndex];

  if (!source || !target) {
    return;
  }

  const [moved] = source.parent.splice(source.index, 1);
  if (!moved) {
    return;
  }

  const updatedFlat = flatten(items.value);
  const updatedTarget = updatedFlat.find(({ item }) => item === target.item);
  if (!updatedTarget) {
    return;
  }

  const insertIndex = oldIndex < newIndex ? updatedTarget.index + 1 : updatedTarget.index;
  updatedTarget.parent.splice(insertIndex, 0, moved);
}

const tree = useTemplateRef<HTMLElement>('tree');

useSortable(tree, items, {
  animation: 150,
  ghostClass: 'opacity-50',
  onUpdate: (e: any) => moveItem(e.oldIndex, e.newIndex),
});
</script>

<template>
  <PTree
    ref="tree"
    :nested="false"
    :unmount-on-hide="false"
    :items="items"
  />
</template>
```

> [!NOTE]
> 
> This example sets the `nested` prop to `false` to have a flat list of items so that the items can be dragged and dropped.

### With virtualization

Use the `virtualize` prop to enable virtualization for large lists as a boolean or an object with options like `{ estimateSize: 32, overscan: 12 }`.

> [!WARNING]
> 
> When virtualization is enabled, the tree structure is flattened, similar to setting the `nested` prop to `false`.

```vue [TreeVirtualizeExample.vue]
<script setup lang="ts">
import type { TreeItem } from 'pohon-ui';

const items: Array<TreeItem> = Array(1000).fill(0).map((_, i) => ({
  label: `Item ${i + 1}`,
  children: [
    { label: `Child ${i + 1}-1`, icon: 'i-lucide-file' },
    { label: `Child ${i + 1}-2`, icon: 'i-lucide-file' },
  ],
}));
</script>

<template>
  <PTree
    virtualize
    :items="items"
    class="h-80"
  />
</template>
```

### With custom slot

Use the `slot` property to customize a specific item.

You will have access to the following slots:

- `#{{ item.slot }}-wrapper`
- `#{{ item.slot }}`
- `#{{ item.slot }}-leading`
- `#{{ item.slot }}-label`
- `#{{ item.slot }}-trailing`

```vue [TreeCustomSlotExample.vue]
<script setup lang="ts">
import type { TreeItem } from 'pohon-ui';

const items = [
  {
    label: 'app/',
    slot: 'app' as const,
    defaultExpanded: true,
    children: [
      {
        label: 'composables/',
        children: [
          { label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
          { label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' },
        ],
      },
      {
        label: 'components/',
        defaultExpanded: true,
        children: [
          { label: 'Card.vue', icon: 'i-vscode-icons-file-type-vue' },
          { label: 'Button.vue', icon: 'i-vscode-icons-file-type-vue' },
        ],
      },
    ],
  },
  { label: 'app.vue', icon: 'i-vscode-icons-file-type-vue' },
  { label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' },
] satisfies Array<TreeItem>;
</script>

<template>
  <PTree :items="items">
    <template #app="{ item }">
      <p class="font-bold italic">
        {{ item.label }}
      </p>
    </template>
  </PTree>
</template>
```

## API

### Props

```ts
/**
 * Props for the Tree component
 */
interface TreeProps {
}
```

### Slots

```ts
/**
 * Slots for the Tree component
 */
interface TreeSlots {
}
```

### Emits

No events available for this component.

## Theme

```ts [app.config.ts]
export default defineAppConfig({
  ui: {
    tree: {
      slots: {
        root: 'relative isolate',
        item: 'w-full',
        listWithChildren: 'border-s border-border',
        itemWithChildren: 'ps-1.5 -ms-px',
        link: 'group text-sm flex w-full select-none items-center relative focus-visible:outline-none focus:outline-none before:rounded-md before:content-empty before:inset-x-0 before:inset-y-px before:absolute before:-z-1 focus-visible:before:outline-3',
        linkLeadingIcon: 'shrink-0 relative',
        linkLabel: 'truncate',
        linkTrailing: 'ms-auto inline-flex gap-1.5 items-center',
        linkTrailingIcon: 'shrink-0 transition-transform-280 ease-out group-data-[expanded]:rotate-180 motion-reduce:transition-none'
      },
      variants: {
        virtualize: {
          true: {
            root: 'overflow-y-auto'
          }
        },
        color: {
          primary: {
            link: 'before:outline-primary/25'
          },
          secondary: {
            link: 'before:outline-secondary/25'
          },
          success: {
            link: 'before:outline-success/25'
          },
          info: {
            link: 'before:outline-info/25'
          },
          warning: {
            link: 'before:outline-warning/25'
          },
          error: {
            link: 'before:outline-error/25'
          },
          neutral: {
            link: 'before:outline-border-inverted/25'
          }
        },
        size: {
          xs: {
            listWithChildren: 'ms-4',
            link: 'px-2 py-1 text-xs gap-1',
            linkLeadingIcon: 'size-4',
            linkTrailingIcon: 'size-4'
          },
          sm: {
            listWithChildren: 'ms-4.5',
            link: 'px-2.5 py-1.5 text-xs gap-1.5',
            linkLeadingIcon: 'size-4',
            linkTrailingIcon: 'size-4'
          },
          md: {
            listWithChildren: 'ms-5',
            link: 'px-2.5 py-1.5 text-sm gap-1.5',
            linkLeadingIcon: 'size-5',
            linkTrailingIcon: 'size-5'
          },
          lg: {
            listWithChildren: 'ms-5.5',
            link: 'px-3 py-2 text-sm gap-2',
            linkLeadingIcon: 'size-5',
            linkTrailingIcon: 'size-5'
          },
          xl: {
            listWithChildren: 'ms-6',
            link: 'px-3 py-2 text-base gap-2',
            linkLeadingIcon: 'size-6',
            linkTrailingIcon: 'size-6'
          }
        },
        selected: {
          true: {
            link: 'before:bg-background-elevated'
          }
        },
        disabled: {
          true: {
            link: 'cursor-not-allowed opacity-75'
          }
        }
      },
      compoundVariants: [
        {
          color: 'primary',
          selected: true,
          class: {
            link: 'color-primary'
          }
        },
        {
          color: 'secondary',
          selected: true,
          class: {
            link: 'color-secondary'
          }
        },
        {
          color: 'success',
          selected: true,
          class: {
            link: 'color-success'
          }
        },
        {
          color: 'info',
          selected: true,
          class: {
            link: 'color-info'
          }
        },
        {
          color: 'warning',
          selected: true,
          class: {
            link: 'color-warning'
          }
        },
        {
          color: 'error',
          selected: true,
          class: {
            link: 'color-error'
          }
        },
        {
          color: 'neutral',
          selected: true,
          class: {
            link: 'color-text-highlighted'
          }
        },
        {
          selected: false,
          disabled: false,
          class: {
            link: [
              'hover:color-text-highlighted hover:before:bg-background-elevated/50',
              'transition-colors before:transition-colors'
            ]
          }
        }
      ],
      defaultVariants: {
        color: 'primary',
        size: 'md'
      }
    }
  }
})
```

## Changelog

See commit history for [component](https://github.com/vinicunca/pohon/commits/main/src/runtime/components/Tree.vue) and [theme](https://github.com/vinicunca/pohon/commits/main/src/theme/tree.ts).

---

- [Tree](https://vinicunca.dev/akar/components/tree)
- [GitHub](https://github.com/vinicunca/pohon/blob/main/src/runtime/components/Tree.vue)


## Sitemap

See the full [sitemap](https://vinicunca.dev/sitemap.md) for all pages.
