UIPackage
Menu

Framework

Change language

Boilerplate repo

Tree View

tree-viewui

Indented tree of expandable nodes — file browsers, taxonomy editors, nested settings. Discord-style elbow connectors, lazy-load branches, and full keyboard navigation.

Also available for React ->

Installation

$npx shadcn-vue@latest add https://uipkge.dev/r/vue/tree-view.json
Named registry:npx shadcn-vue@latest add @uipkge/tree-viewInstalls to:app/components/ui/tree-view/

Examples

Loading interactive previews…

Props

NameType / ValuesDefaultRequired
itemsTreeViewItem[]required
classHTMLAttributes['class']optional
showIconsbooleantrueoptional
showCheckboxesbooleanfalseoptional
defaultExpandedbooleanfalseoptional
selectedIdstring | nullnulloptional

Schema

Type aliases from this item's source — use them to shape the data you pass in.

TreeViewContext
interface TreeViewContext {
  expandedIds: Ref<Set<string>>
  selectedId: Ref<string | null>
  showIcons: Ref<boolean>
  showCheckboxes: Ref<boolean>
  toggle: (item: TreeViewItem) => void
  select: (item: TreeViewItem) => void
}
TreeViewItem
interface TreeViewItem {
  id: string
  label: string
  icon?: typeof File
  children?: TreeViewItem[]
  disabled?: boolean
  selected?: boolean
  expanded?: boolean
  [key: string]: unknown
}

npm dependencies

Files installed (5)

  • app/components/ui/tree-view/TreeView.vue2.1 kB
    <script setup lang="ts">
    import { onMounted, provide, ref, toRef, watch, type HTMLAttributes } from 'vue'
    import { cn } from '@/lib/utils'
    import { TREE_VIEW_CONTEXT } from './context'
    import type { TreeViewItem } from './types'
    // Explicit import required for non-Nuxt consumers (shadcn-vue copies source as-is).
    import TreeViewNode from './TreeViewNode.vue'
    
    interface Props {
      items: TreeViewItem[]
      class?: HTMLAttributes['class']
      showIcons?: boolean
      showCheckboxes?: boolean
      defaultExpanded?: boolean
      selectedId?: string | null
    }
    
    const props = withDefaults(defineProps<Props>(), {
      showIcons: true,
      showCheckboxes: false,
      defaultExpanded: false,
      selectedId: null,
    })
    
    const emit = defineEmits<{
      select: [item: TreeViewItem]
      toggle: [item: TreeViewItem]
      'update:selectedId': [id: string | null]
    }>()
    
    const expandedIds = ref<Set<string>>(new Set())
    const selectedId = ref<string | null>(props.selectedId)
    
    watch(
      () => props.selectedId,
      (val) => {
        selectedId.value = val
      },
    )
    
    function toggle(item: TreeViewItem) {
      if (item.disabled) return
      const next = new Set(expandedIds.value)
      if (next.has(item.id)) next.delete(item.id)
      else next.add(item.id)
      expandedIds.value = next
      emit('toggle', item)
    }
    
    function select(item: TreeViewItem) {
      if (item.disabled) return
      selectedId.value = item.id
      emit('select', item)
      emit('update:selectedId', item.id)
    }
    
    provide(TREE_VIEW_CONTEXT, {
      expandedIds,
      selectedId,
      showIcons: toRef(props, 'showIcons'),
      showCheckboxes: toRef(props, 'showCheckboxes'),
      toggle,
      select,
    })
    
    onMounted(() => {
      if (!props.defaultExpanded) return
      const next = new Set<string>()
      const walk = (items: TreeViewItem[]) => {
        for (const it of items) {
          if (it.children?.length) {
            next.add(it.id)
            walk(it.children)
          }
        }
      }
      walk(props.items)
      expandedIds.value = next
    })
    </script>
    
    <template>
      <div :class="cn('text-sm', props.class)" role="tree">
        <TreeViewNode v-for="(item, i) in items" :key="item.id" :item="item" :depth="0" :is-last="i === items.length - 1" />
      </div>
    </template>
    
  • app/components/ui/tree-view/TreeViewNode.vue6.7 kB
  • app/components/ui/tree-view/context.ts0.4 kB
  • app/components/ui/tree-view/types.ts0.2 kB
  • app/components/ui/tree-view/index.ts0.2 kB

Raw manifest:https://uipkge.dev/r/vue/tree-view.json