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>
<UTree :items="items" />
</template>
Use the items prop as an array of objects with the following properties:
icon?: stringlabel?: stringtrailingIcon?: stringdefaultExpanded?: booleandisabled?: booleanslot?: stringchildren?: TreeItem[]onToggle?: (e: TreeItemToggleEvent<TreeItem>) => voidonSelect?: (e: TreeItemSelectEvent<TreeItem>) => voidclass?: anyui?: { 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 '@nuxt/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>
<UTree :items="items" />
</template>
Use the multiple prop to allow multiple item selections.
<script setup lang="ts">
import type { TreeItem } from '@nuxt/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>
<UTree multiple :items="items" />
</template>
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 '@nuxt/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>
<UTree :nested="false" :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.
Use the color prop to change the color of the Tree.
<script setup lang="ts">
import type { TreeItem } from '@nuxt/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>
<UTree color="neutral" :items="items" />
</template>
Use the size prop to change the size of the Tree.
<script setup lang="ts">
import type { TreeItem } from '@nuxt/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>
<UTree size="xl" :items="items" />
</template>
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 '@nuxt/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>
<UTree trailing-icon="i-lucide-arrow-down" :items="items" />
</template>
You can customize this icon globally in your app.config.ts under ui.icons.chevronDown key.
You can customize this icon globally in your vite.config.ts under ui.icons.chevronDown key.
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 '@nuxt/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>
<UTree 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 ui.icons.folder and ui.icons.folderOpen keys.
You can customize these icons globally in your vite.config.ts under ui.icons.folder and ui.icons.folderOpen keys.
Use the disabled prop to prevent any user interaction with the Tree.
<script setup lang="ts">
import type { TreeItem } from '@nuxt/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>
<UTree disabled :items="items" />
</template>
You can also disable individual items using item.disabled.
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()
</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 or the global select event:
<script setup lang="ts">
import type { TreeItemSelectEvent } from 'reka-ui'
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' }
]
function onSelect(e: TreeItemSelectEvent<TreeItem>) {
if (e.detail.originalEvent.type === 'click') {
e.preventDefault()
}
}
</script>
<template>
<UTree :items="items" @select="onSelect" />
</template>
This lets you expand or collapse a parent item without selecting it.
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 = [
{
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 TreeItem[]
const expanded = ref(['app', 'app/composables'])
</script>
<template>
<UTree 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 { TreeItemToggleEvent } from 'reka-ui'
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' }
]
function onToggle(e: TreeItemToggleEvent<TreeItem>) {
if (e.detail.originalEvent.type === 'keydown') {
e.preventDefault()
}
}
</script>
<template>
<UTree :items="items" @toggle="onToggle" />
</template>
This lets you select a parent item without expanding or collapsing its children.
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 { TreeItemSelectEvent } from 'reka-ui'
import type { TreeItem } from '@nuxt/ui'
const items: 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>
<UTree
v-model="value"
:as="{ link: 'div' }"
:items="items"
multiple
propagate-select
bubble-select
@select="onSelect"
>
<template #item-leading="{ selected, indeterminate, handleSelect }">
<UCheckbox
:model-value="indeterminate ? 'indeterminate' : selected"
tabindex="-1"
@change="handleSelect"
@click.stop
/>
</template>
</UTree>
</template>
This example uses the as prop to change the items from button to div as the Checkbox is also rendered as a button.
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 { TreeItem } from '@nuxt/ui'
import { useSortable } from '@vueuse/integrations/useSortable'
const items = shallowRef<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: TreeItem[], parent = items): { item: TreeItem, parent: 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>
<UTree 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.
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 { TreeItem } from '@nuxt/ui'
const items: 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>
<UTree virtualize :items="items" class="h-80" />
</template>
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 { TreeItem } from '@nuxt/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 TreeItem[]
</script>
<template>
<UTree :items="items">
<template #app="{ item }">
<p class="italic font-bold">
{{ item.label }}
</p>
</template>
</UTree>
</template>
| Prop | Default | Type |
|---|---|---|
as |
|
The element or component this component should render as. |
color |
|
|
size |
|
|
getKey |
This function is passed the index of each item and should return a unique key for that 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. | |
nested |
|
Use nested DOM structure (children inside parents) vs flattened structure (all items at same level).
When |
virtualize |
|
Enable virtualization for large lists.
Note: when enabled, the tree structure is flattened like if
|
onSelect |
| |
onToggle |
| |
expanded | The controlled value of the expanded item. Can be binded with with | |
defaultExpanded |
The value of the expanded tree when initially rendered. Use when you do not need to control the state of the expanded tree | |
selectionBehavior |
How multiple selection should behave in the collection. | |
propagateSelect |
When | |
disabled |
When | |
bubbleSelect |
When | |
ui |
|
| Slot | Type |
|---|---|
item-wrapper | |
item | |
item-leading | |
item-label | |
item-trailing |
|
| Event | Type |
|---|---|
update:modelValue |
|
update:expanded |
|
export default defineAppConfig({
ui: {
tree: {
slots: {
root: 'relative isolate',
item: 'w-full',
listWithChildren: 'border-s border-default',
itemWithChildren: 'ps-1.5 -ms-px',
link: 'relative group w-full flex items-center text-sm select-none before:absolute before:inset-y-px before:inset-x-0 before:z-[-1] before:rounded-md focus:outline-none focus-visible:outline-none focus-visible:before:ring-inset focus-visible:before:ring-2',
linkLeadingIcon: 'shrink-0 relative',
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: {
virtualize: {
true: {
root: 'overflow-y-auto'
}
},
color: {
primary: {
link: 'focus-visible:before:ring-primary'
},
secondary: {
link: 'focus-visible:before:ring-secondary'
},
success: {
link: 'focus-visible:before:ring-success'
},
info: {
link: 'focus-visible:before:ring-info'
},
warning: {
link: 'focus-visible:before:ring-warning'
},
error: {
link: 'focus-visible:before:ring-error'
},
neutral: {
link: 'focus-visible:before:ring-inverted'
}
},
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-elevated'
}
},
disabled: {
true: {
link: 'cursor-not-allowed opacity-75'
}
}
},
compoundVariants: [
{
color: 'primary',
selected: true,
class: {
link: 'text-primary'
}
},
{
color: 'neutral',
selected: true,
class: {
link: 'text-highlighted'
}
},
{
selected: false,
disabled: false,
class: {
link: [
'hover:text-highlighted hover:before:bg-elevated/50',
'transition-colors before:transition-colors'
]
}
}
],
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: 'w-full',
listWithChildren: 'border-s border-default',
itemWithChildren: 'ps-1.5 -ms-px',
link: 'relative group w-full flex items-center text-sm select-none before:absolute before:inset-y-px before:inset-x-0 before:z-[-1] before:rounded-md focus:outline-none focus-visible:outline-none focus-visible:before:ring-inset focus-visible:before:ring-2',
linkLeadingIcon: 'shrink-0 relative',
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: {
virtualize: {
true: {
root: 'overflow-y-auto'
}
},
color: {
primary: {
link: 'focus-visible:before:ring-primary'
},
secondary: {
link: 'focus-visible:before:ring-secondary'
},
success: {
link: 'focus-visible:before:ring-success'
},
info: {
link: 'focus-visible:before:ring-info'
},
warning: {
link: 'focus-visible:before:ring-warning'
},
error: {
link: 'focus-visible:before:ring-error'
},
neutral: {
link: 'focus-visible:before:ring-inverted'
}
},
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-elevated'
}
},
disabled: {
true: {
link: 'cursor-not-allowed opacity-75'
}
}
},
compoundVariants: [
{
color: 'primary',
selected: true,
class: {
link: 'text-primary'
}
},
{
color: 'neutral',
selected: true,
class: {
link: 'text-highlighted'
}
},
{
selected: false,
disabled: false,
class: {
link: [
'hover:text-highlighted hover:before:bg-elevated/50',
'transition-colors before:transition-colors'
]
}
}
],
defaultVariants: {
color: 'primary',
size: 'md'
}
}
}
})
]
})
Some colors in compoundVariants are omitted for readability. Check out the source code on GitHub.