Menu

Change language

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

Installation

$npx shadcn-svelte@latest add https://uipkge.dev/r/svelte/tree-view.json
Manifest:https://uipkge.dev/r/svelte/tree-view.jsonInstalls to:src/lib/components/ui/tree-view/

Examples

Loading interactive previews…

Props

NameType / ValuesDefaultRequired
itemsTreeViewItem[]—required
showIconsbooleantrueoptional
showCheckboxesbooleanfalseoptional
defaultExpandedbooleanfalseoptional
selectedIdstring | null—optional
onselect(item: TreeViewItem) => void—optional
ontoggle(item: TreeViewItem) => void—optional
onSelectedIdChange(id: string | null) => void—optional
refHTMLDivElement | null—optional

Schema

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

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

npm dependencies

Files installed (5)

  • src/lib/components/ui/tree-view/TreeView.svelte2.4 kB
    <script lang="ts" module>
      import type { HTMLAttributes } from 'svelte/elements'
      import type { TreeViewItem } from './types'
    
      // Omit DOM event props shadowed by component callbacks (item payloads, not events).
      export interface TreeViewProps extends Omit<HTMLAttributes<HTMLDivElement>, 'onselect' | 'ontoggle'> {
        items: TreeViewItem[]
        showIcons?: boolean
        showCheckboxes?: boolean
        defaultExpanded?: boolean
        selectedId?: string | null
        onselect?: (item: TreeViewItem) => void
        ontoggle?: (item: TreeViewItem) => void
        onSelectedIdChange?: (id: string | null) => void
        ref?: HTMLDivElement | null
      }
    </script>
    
    <script lang="ts">
      import { onMount } from 'svelte'
      import { cn } from '$lib/utils'
      import { setTreeViewContext } from './context'
      import TreeViewNode from './TreeViewNode.svelte'
    
      let {
        items,
        showIcons = true,
        showCheckboxes = false,
        defaultExpanded = false,
        selectedId = $bindable(null),
        onselect,
        ontoggle,
        onSelectedIdChange,
        class: className,
        ref = $bindable(null),
        ...restProps
      }: TreeViewProps = $props()
    
      let expandedIds = $state<Set<string>>(new Set())
    
      function toggle(item: TreeViewItem) {
        if (item.disabled) return
        const next = new Set(expandedIds)
        if (next.has(item.id)) next.delete(item.id)
        else next.add(item.id)
        expandedIds = next
        ontoggle?.(item)
      }
    
      function select(item: TreeViewItem) {
        if (item.disabled) return
        selectedId = item.id
        onselect?.(item)
        onSelectedIdChange?.(item.id)
      }
    
      // Getters keep the child rows reactive to parent state without a .svelte.ts store.
      setTreeViewContext({
        get expandedIds() {
          return expandedIds
        },
        get selectedId() {
          return selectedId
        },
        get showIcons() {
          return showIcons
        },
        get showCheckboxes() {
          return showCheckboxes
        },
        toggle,
        select,
      })
    
      onMount(() => {
        if (!defaultExpanded) return
        const next = new Set<string>()
        const walk = (list: TreeViewItem[]) => {
          for (const it of list) {
            if (it.children?.length) {
              next.add(it.id)
              walk(it.children)
            }
          }
        }
        walk(items)
        expandedIds = next
      })
    </script>
    
    <div
      bind:this={ref}
      data-uipkge
      data-slot="tree-view"
      role="tree"
      class={cn('text-sm', className)}
      {...restProps}
    >
      {#each items as item, i (item.id)}
        <TreeViewNode {item} depth={0} isLast={i === items.length - 1} />
      {/each}
    </div>
    
  • src/lib/components/ui/tree-view/TreeViewNode.svelte6.6 kB
  • src/lib/components/ui/tree-view/context.ts0.7 kB
  • src/lib/components/ui/tree-view/types.ts0.2 kB
  • src/lib/components/ui/tree-view/index.ts0.2 kB

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