Skip to content

Tree

An expandable, selectable hierarchy - file browsers, nested settings, org structures - built on Reka UI's Tree primitive.

Usage

items is a nested array - a node with children renders as an expandable row, one without renders as a leaf. Click a row to expand it (and select it, if it's a leaf):

  • Notes.txt
vue
<script setup lang="ts">
const items = [
  {
    label: 'Documents',
    value: 'documents',
    children: [
      { label: 'Resume.pdf', value: 'resume' },
      { label: 'Cover Letter.docx', value: 'cover-letter' },
    ],
  },
  { label: 'Notes.txt', value: 'notes' },
]
</script>

<template>
  <STree :items="items" />
</template>

Multiple selection

multiple makes v-model an array instead of a single node. default-expanded (an array of keys) opens branches immediately, no click needed:

  • Frontend
  • Vue
  • Svelte
  • Backend
  • Node
  • Go

0 selected

vue-html
<STree v-model="selected" :items="items" multiple :default-expanded="['frontend', 'backend']" />

Checkbox mode

checkbox shows a tri-state checkbox per row (reusing SCheckbox's own checked/unchecked/indeterminate model) and turns on multiple together with parent↔child propagation: selecting a parent checks every one of its descendants, and checking all of a node's children checks the parent too (partially checking them marks it indeterminate instead):

  • Permissions
  • Read
  • Write
  • Admin
vue-html
<STree v-model="selected" :items="items" checkbox :default-expanded="['permissions']" />

Custom item content

The item slot replaces a row's default icon+label content, scoped with the node itself plus its live expanded/selected/indeterminate state:

  • Reports
  • Daily3
  • Weekly12
vue-html
<STree :items="items">
  <template #item="{ item }">
    <span class="flex flex-1 items-center justify-between">
      <span>{{ item.label }}</span>
      <SBadge v-if="item.badge" :label="item.badge" size="sm" variant="soft" />
    </span>
  </template>
</STree>

Disabled items

A node's own disabled: true blocks just that row - siblings stay interactive:

  • Team
  • Members
  • Billing
  • Danger zone
vue-html
<STree :items="items" />
ts
const items = [
  { label: 'Members', value: 'members' },
  { label: 'Billing', value: 'billing', disabled: true },
]

Custom :ui

To see exactly what you'd be overriding - the current default classes for every slot and variant - here's Tree's own theme file:

Theme source "tree" not found

Props

PropTypeDefault
itemsTreeItemType[]- (required)
modelValueTreeItemType | TreeItemType[]-
defaultValueTreeItemType | TreeItemType[]-
expandedstring[]-
defaultExpandedstring[]-
multiplebooleanfalse
checkboxbooleanfalse
propagateSelectbooleanfalse
bubbleSelectbooleanfalse
disabledbooleanfalse
getKey(item: TreeItemType) => stringitem => item.value ?? item.label
getChildren(item: TreeItemType) => TreeItemType[] | undefineditem => item.children
size'sm' | 'md' | 'lg'md
uiPartial<Record<TreeSlot, string | object>>-

Events

EventPayloadDescription
update:modelValueTreeItemType | TreeItemType[]Fires on selection
update:expandedstring[]Fires when a row is expanded or collapsed

Slots

SlotPropsDescription
item{ item, level, expanded, selected, indeterminate }Replaces a row's default icon+label content
Edit this page