Usage
Items
Use the items prop as an array of objects with the following properties:
icon?: stringlabel?: stringtrailingIcon?: stringdefaultExpanded?: booleandisabled?: booleanvalue?: stringslot?: stringchildren?: TreeItem[]onToggle?(e: Event): voidonSelect?(e?: Event): void
value prop as identifier, falling back to label if value is not provided. One of these must be provided for the component to work properly.<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>
  <UTree :items="items" />
</template>
Color
Use the color prop to change the color of the Tree.
<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>
  <UTree color="neutral" :items="items" />
</template>
Size
Use the size prop to change the size of the Tree.
<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>
  <UTree size="xl" :items="items" />
</template>
Multiple
Use the multiple prop to allow multiple item selections.
<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>
  <UTree multiple :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.
<script setup lang="ts">
const items = ref([
  {
    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>
  <UTree trailing-icon="i-lucide-arrow-down" :items="items" />
</template>
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">
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>
  <UTree expanded-icon="i-lucide-book-open" collapsed-icon="i-lucide-book" :items="items" />
</template>
app.config.ts under ui.icons.folder and ui.icons.folderOpen keys.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.
item.disabled.<script setup lang="ts">
const items = ref([
  {
    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>
  <UTree disabled :items="items" />
</template>
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 { TreeItem } from '@nuxt/ui'
const items: 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(items[items.length - 1])
</script>
<template>
  <UTree v-model="value" :items="items" />
</template>
If you want to prevent an item from being selected, you can use the item.onSelect() property:
<script setup lang="ts">
import type { TreeItem } from '@nuxt/ui'
const items: 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' }
]
</script>
<template>
  <UTree :items="items" />
</template>
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 { TreeItem } from '@nuxt/ui'
const items: TreeItem[] = [
  {
    label: 'app/',
    value: 'app',
    children: [{
      label: 'composables/',
      value: 'composables',
      children: [
        { label: 'useAuth.ts', icon: 'i-vscode-icons-file-type-typescript' },
        { label: 'useUser.ts', icon: 'i-vscode-icons-file-type-typescript' }
      ]
    },
    {
      label: 'components/',
      value: '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', icon: 'i-vscode-icons-file-type-vue' },
  { label: 'nuxt.config.ts', icon: 'i-vscode-icons-file-type-nuxt' }
]
const expanded = ref(['app', 'composables'])
</script>
<template>
  <UTree v-model:expanded="expanded" :items="items" />
</template>
If you want to prevent an item from being expanded, you can use the item.onToggle() property:
<script setup lang="ts">
import type { TreeItem } from '@nuxt/ui'
const items: 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' }
]
</script>
<template>
  <UTree :items="items" />
</template>
With custom slot
Use the item.slot property to customize a specific item.
<script setup lang="ts">
import type { TreeItem } from '@nuxt/ui'
const items: TreeItem[] = [
  {
    label: 'app/',
    slot: '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>
  <UTree :items="items">
    <template #app="{ item }">
      <p class="italic font-bold">
        {{ item.label }}
      </p>
    </template>
  </UTree>
</template>
API
Props
| Prop | Default | Type | 
|---|---|---|
as | 
  | 
 The element or component this component should render as.  | 
color | 
  | 
  | 
size | 
  | 
  | 
valueKey | 
  | 
 The key used to get the value from the item.  | 
labelKey | 
  | 
 The key used to get the label from the item.  | 
trailingIcon | 
  | 
 The icon displayed on the right side of a parent node.  | 
expandedIcon | 
  | 
 The icon displayed when a parent node is expanded.  | 
collapsedIcon | 
  | 
 The icon displayed when a parent node is collapsed.  | 
items | 
 
  | |
modelValue | 
 The controlled value of the Tree. Can be bind as  
  | |
defaultValue | 
 The value of the Tree when initially rendered. Use when you do not need to control the state of the Tree. 
  | |
multiple | 
 Whether multiple options can be selected or not.  | |
disabled | 
 When   | |
expanded | 
 The controlled value of the expanded item. Can be binded with with   | |
selectionBehavior | 
 How multiple selection should behave in the collection.  | |
defaultExpanded | 
 The value of the expanded tree when initially rendered. Use when you do not need to control the state of the expanded tree  | |
propagateSelect | 
 When   | |
ui | 
  | 
Slots
| Slot | Type | 
|---|---|
item | 
  | 
item-leading | 
  | 
item-label | 
  | 
item-trailing | 
  | 
Emits
| Event | Type | 
|---|---|
update:modelValue | 
  | 
update:expanded | 
  | 
Theme
export default defineAppConfig({
  ui: {
    tree: {
      slots: {
        root: 'relative isolate',
        item: '',
        listWithChildren: 'ms-4.5 border-s border-(--ui-border)',
        itemWithChildren: 'ps-1.5 -ms-px',
        link: 'relative group w-full flex items-center text-sm before:absolute before:inset-y-px before:inset-x-0 before:z-[-1] before:rounded-[calc(var(--ui-radius)*1.5)] focus:outline-none focus-visible:outline-none focus-visible:before:ring-inset focus-visible:before:ring-2',
        linkLeadingIcon: 'shrink-0',
        linkLabel: 'truncate',
        linkTrailing: 'ms-auto inline-flex gap-1.5 items-center',
        linkTrailingIcon: 'shrink-0 transform transition-transform duration-200 group-data-expanded:rotate-180'
      },
      variants: {
        color: {
          primary: {
            link: 'focus-visible:before:ring-(--ui-primary)'
          },
          secondary: {
            link: 'focus-visible:before:ring-(--ui-secondary)'
          },
          success: {
            link: 'focus-visible:before:ring-(--ui-success)'
          },
          info: {
            link: 'focus-visible:before:ring-(--ui-info)'
          },
          warning: {
            link: 'focus-visible:before:ring-(--ui-warning)'
          },
          error: {
            link: 'focus-visible:before:ring-(--ui-error)'
          },
          neutral: {
            link: 'focus-visible:before:ring-(--ui-border-inverted)'
          }
        },
        size: {
          xs: {
            link: 'px-2 py-1 text-xs gap-1',
            linkLeadingIcon: 'size-4',
            linkTrailingIcon: 'size-4'
          },
          sm: {
            link: 'px-2.5 py-1.5 text-xs gap-1.5',
            linkLeadingIcon: 'size-4',
            linkTrailingIcon: 'size-4'
          },
          md: {
            link: 'px-2.5 py-1.5 text-sm gap-1.5',
            linkLeadingIcon: 'size-5',
            linkTrailingIcon: 'size-5'
          },
          lg: {
            link: 'px-3 py-2 text-sm gap-2',
            linkLeadingIcon: 'size-5',
            linkTrailingIcon: 'size-5'
          },
          xl: {
            link: 'px-3 py-2 text-base gap-2',
            linkLeadingIcon: 'size-6',
            linkTrailingIcon: 'size-6'
          }
        },
        selected: {
          true: {
            link: 'before:bg-(--ui-bg-elevated)'
          },
          false: {
            link: [
              'hover:not-disabled:text-(--ui-text-highlighted) hover:not-disabled:before:bg-(--ui-bg-elevated)/50',
              'transition-colors before:transition-colors'
            ]
          }
        }
      },
      compoundVariants: [
        {
          color: 'primary',
          selected: true,
          class: {
            link: 'text-(--ui-primary)'
          }
        },
        {
          color: 'neutral',
          selected: true,
          class: {
            link: 'text-(--ui-text-highlighted)'
          }
        }
      ],
      defaultVariants: {
        color: 'primary',
        size: 'md'
      }
    }
  }
})
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import ui from '@nuxt/ui/vite'
export default defineConfig({
  plugins: [
    vue(),
    ui({
      ui: {
        tree: {
          slots: {
            root: 'relative isolate',
            item: '',
            listWithChildren: 'ms-4.5 border-s border-(--ui-border)',
            itemWithChildren: 'ps-1.5 -ms-px',
            link: 'relative group w-full flex items-center text-sm before:absolute before:inset-y-px before:inset-x-0 before:z-[-1] before:rounded-[calc(var(--ui-radius)*1.5)] focus:outline-none focus-visible:outline-none focus-visible:before:ring-inset focus-visible:before:ring-2',
            linkLeadingIcon: 'shrink-0',
            linkLabel: 'truncate',
            linkTrailing: 'ms-auto inline-flex gap-1.5 items-center',
            linkTrailingIcon: 'shrink-0 transform transition-transform duration-200 group-data-expanded:rotate-180'
          },
          variants: {
            color: {
              primary: {
                link: 'focus-visible:before:ring-(--ui-primary)'
              },
              secondary: {
                link: 'focus-visible:before:ring-(--ui-secondary)'
              },
              success: {
                link: 'focus-visible:before:ring-(--ui-success)'
              },
              info: {
                link: 'focus-visible:before:ring-(--ui-info)'
              },
              warning: {
                link: 'focus-visible:before:ring-(--ui-warning)'
              },
              error: {
                link: 'focus-visible:before:ring-(--ui-error)'
              },
              neutral: {
                link: 'focus-visible:before:ring-(--ui-border-inverted)'
              }
            },
            size: {
              xs: {
                link: 'px-2 py-1 text-xs gap-1',
                linkLeadingIcon: 'size-4',
                linkTrailingIcon: 'size-4'
              },
              sm: {
                link: 'px-2.5 py-1.5 text-xs gap-1.5',
                linkLeadingIcon: 'size-4',
                linkTrailingIcon: 'size-4'
              },
              md: {
                link: 'px-2.5 py-1.5 text-sm gap-1.5',
                linkLeadingIcon: 'size-5',
                linkTrailingIcon: 'size-5'
              },
              lg: {
                link: 'px-3 py-2 text-sm gap-2',
                linkLeadingIcon: 'size-5',
                linkTrailingIcon: 'size-5'
              },
              xl: {
                link: 'px-3 py-2 text-base gap-2',
                linkLeadingIcon: 'size-6',
                linkTrailingIcon: 'size-6'
              }
            },
            selected: {
              true: {
                link: 'before:bg-(--ui-bg-elevated)'
              },
              false: {
                link: [
                  'hover:not-disabled:text-(--ui-text-highlighted) hover:not-disabled:before:bg-(--ui-bg-elevated)/50',
                  'transition-colors before:transition-colors'
                ]
              }
            }
          },
          compoundVariants: [
            {
              color: 'primary',
              selected: true,
              class: {
                link: 'text-(--ui-primary)'
              }
            },
            {
              color: 'neutral',
              selected: true,
              class: {
                link: 'text-(--ui-text-highlighted)'
              }
            }
          ],
          defaultVariants: {
            color: 'primary',
            size: 'md'
          }
        }
      }
    })
  ]
})