Tree

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

Usage

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

<script setup lang="ts">
const items = ref([
  {
    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
  • pohon?: { item?: ClassNameValue, itemWithChildren?: ClassNameValue, link?: ClassNameValue, linkLeadingIcon?: ClassNameValue, linkLabel?: ClassNameValue, linkTrailing?: ClassNameValue, linkTrailingIcon?: ClassNameValue, listWithChildren?: ClassNameValue }
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.
<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.

<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.

<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>
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.

<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.

<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 of a parent node. Defaults to i-lucide:chevron-down.

If an icon is specified for an item, it will always take precedence over these props.
<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>
You can customize this icon globally in your app.config.ts under pohon.icons.chevronDown key.
You can customize this icon globally in your vite.config.ts under pohon.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.

<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>
You can customize these icons globally in your app.config.ts under pohon.icons.folder and pohon.icons.folderOpen keys.
You can customize these icons globally in your vite.config.ts under pohon.icons.folder and pohon.icons.folderOpen keys.

Disabled

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

<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>
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.

<script setup lang="ts">
import type { PTreeItem } from 'pohon-ui';
import { ref } from 'vue';

const items: Array<PTreeItem> = [
  {
    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>

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

<script setup lang="ts">
import type { ATreeItemSelectEvent } from 'akar';
import type { PTreeItem } from 'pohon-ui';

const items: Array<PTreeItem> = [
  {
    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: ATreeItemSelectEvent<PTreeItem>) {
  if (e.detail.originalEvent.type === 'click') {
    e.preventDefault();
  }
}
</script>

<template>
  <PTree
    :items="items"
    @select="onSelect"
  />
</template>
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.

<script setup lang="ts">
import type { PTreeItem } from 'pohon-ui';
import { ref } from 'vue';

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<PTreeItem>;

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:

<script setup lang="ts">
import type { ATreeItemToggleEvent } from 'akar';
import type { PTreeItem } from 'pohon-ui';

const items: Array<PTreeItem> = [
  {
    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: ATreeItemToggleEvent<PTreeItem>) {
  if (e.detail.originalEvent.type === 'keydown') {
    e.preventDefault();
  }
}
</script>

<template>
  <PTree
    :items="items"
    @toggle="onToggle"
  />
</template>
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 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.

<script setup lang="ts">
import type { ATreeItemSelectEvent } from 'akar';
import type { PTreeItem } from 'pohon-ui';
import { ref } from 'vue';

const items: Array<PTreeItem> = [
  {
    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: ATreeItemSelectEvent<PTreeItem>) {
  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>
This example uses the as prop to change the items from button to div as the Checkbox is also rendered as a button.

With drag and drop

Use the useSortable composable from @vueuse/integrations to enable drag and drop functionality on the Tree. This integration wraps Sortable.js to provide a seamless drag and drop experience.

<script setup lang="ts">
import type { PTreeItem } from 'pohon-ui'
import { useSortable } from '@vueuse/integrations/useSortable'
import { shallowRef, useTemplateRef } from 'vue'

const items = shallowRef<Array<PTreeItem>>([
  {
    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<PTreeItem>,
  parent = items
): Array<{ item: PTreeItem; parent: Array<PTreeItem>; 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>
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 }.

When virtualization is enabled, the tree structure is flattened, similar to setting the nested prop to false.
<script setup lang="ts">
import type { PTreeItem } from 'pohon-ui'

const items: Array<PTreeItem> = 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
<script setup lang="ts">
import type { PTreeItem } 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<PTreeItem>;
</script>

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

API

Props

Prop Default Type

Slots

Slot Type

Emits

Event Type

Theme

We use unocss-variants to customize the theme. Read more about it in the theming guide.

Below is the theme configuration skeleton for the PTree. Since the component is provided unstyled by default, you will need to fill in these values to apply your own custom look and feel. If you prefer to use our pre-built, opinionated styling, you can instead use our UnoCSS preset, this docs is using it as well.

app.config.ts
export default defineAppConfig({
  pohon: {
    tree: {
      slots: {
        root: '',
        item: '',
        listWithChildren: '',
        itemWithChildren: '',
        link: '',
        linkLeadingIcon: '',
        linkLabel: '',
        linkTrailing: '',
        linkTrailingIcon: ''
      },
      variants: {
        virtualize: {
          true: {
            root: ''
          }
        },
        color: {
          primary: {
            link: ''
          },
          secondary: {
            link: ''
          },
          success: {
            link: ''
          },
          info: {
            link: ''
          },
          warning: {
            link: ''
          },
          error: {
            link: ''
          },
          neutral: {
            link: ''
          }
        },
        size: {
          xs: {
            listWithChildren: '',
            link: '',
            linkLeadingIcon: '',
            linkTrailingIcon: ''
          },
          sm: {
            listWithChildren: '',
            link: '',
            linkLeadingIcon: '',
            linkTrailingIcon: ''
          },
          md: {
            listWithChildren: '',
            link: '',
            linkLeadingIcon: '',
            linkTrailingIcon: ''
          },
          lg: {
            listWithChildren: '',
            link: '',
            linkLeadingIcon: '',
            linkTrailingIcon: ''
          },
          xl: {
            listWithChildren: '',
            link: '',
            linkLeadingIcon: '',
            linkTrailingIcon: ''
          }
        },
        selected: {
          true: {
            link: ''
          },
          false: {
            link: ''
          }
        },
        disabled: {
          true: {
            link: ''
          }
        }
      },
      compoundVariants: [],
      defaultVariants: {
        color: 'primary',
        size: 'md'
      }
    }
  }
};
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import pohon from 'pohon-ui/vite'

export default defineAppConfig({
  pohon: {
    tree: {
      slots: {
        root: '',
        item: '',
        listWithChildren: '',
        itemWithChildren: '',
        link: '',
        linkLeadingIcon: '',
        linkLabel: '',
        linkTrailing: '',
        linkTrailingIcon: ''
      },
      variants: {
        virtualize: {
          true: {
            root: ''
          }
        },
        color: {
          primary: {
            link: ''
          },
          secondary: {
            link: ''
          },
          success: {
            link: ''
          },
          info: {
            link: ''
          },
          warning: {
            link: ''
          },
          error: {
            link: ''
          },
          neutral: {
            link: ''
          }
        },
        size: {
          xs: {
            listWithChildren: '',
            link: '',
            linkLeadingIcon: '',
            linkTrailingIcon: ''
          },
          sm: {
            listWithChildren: '',
            link: '',
            linkLeadingIcon: '',
            linkTrailingIcon: ''
          },
          md: {
            listWithChildren: '',
            link: '',
            linkLeadingIcon: '',
            linkTrailingIcon: ''
          },
          lg: {
            listWithChildren: '',
            link: '',
            linkLeadingIcon: '',
            linkTrailingIcon: ''
          },
          xl: {
            listWithChildren: '',
            link: '',
            linkLeadingIcon: '',
            linkTrailingIcon: ''
          }
        },
        selected: {
          true: {
            link: ''
          },
          false: {
            link: ''
          }
        },
        disabled: {
          true: {
            link: ''
          }
        }
      },
      compoundVariants: [],
      defaultVariants: {
        color: 'primary',
        size: 'md'
      }
    }
  }
};

Akar

With Pohon UI, you can achieve similar component functionality with less code and effort, as it comes with built-in styles mechanism and behaviors that are optimized for common use cases. Since it's using unocss-variants it adds a runtime cost, but it can be worth it if you prioritize development speed and ease of use over fine-grained control.

If this is a deal breaker for you, you can always stick to using Akar and build your own custom components on top of it.

Changelog

No recent changes