Tree

A hierarchical tree component for displaying nested data structures with expand/collapse, selection, and checkbox support.

Component Features

  • 🌳 Hierarchical Display: Displays nested data with expand/collapse support.
  • ✔️ Checkable Mode: Enables checkbox mode via checkable prop with parent-child linkage.
  • 🔗 Selectable Mode: Supports single and multiple selection modes for nodes.
  • 🚫 Disabled State: Supports disabled for individual nodes or entire tree.
  • ➡️ Connection Lines: Supports showLine to display hierarchical connection lines.
  • 🔍 Node Filtering: Filters visible nodes via filterTreeNode.
  • Virtual Scrolling: Renders only visible nodes via virtualScroll for large datasets.
  • Drag & Drop: Supports drag-and-drop node reordering.

Basic

Pass nested data via the data prop. Each node requires a unique key and a label. Click the arrow icon to expand/collapse child nodes.

<script setup lang="ts">
import { ref } from 'vue'
import { BrTree } from '@breezeui/vue'
import type { TreeNodeData } from '@breezeui/vue'

const treeData = ref<TreeNodeData[]>([
  {
    key: '1',
    label: 'Documents',
    children: [
      {
        key: '1-1',
        label: 'Work',
        children: [
          { key: '1-1-1', label: 'project.pdf' },
          { key: '1-1-2', label: 'report.docx' },
        ],
      },
      {
        key: '1-2',
        label: 'Personal',
        children: [
          { key: '1-2-1', label: 'resume.pdf' },
          { key: '1-2-2', label: 'budget.xlsx' },
        ],
      },
    ],
  },
  {
    key: '2',
    label: 'Pictures',
    children: [
      { key: '2-1', label: 'vacation.jpg' },
      { key: '2-2', label: 'family.png' },
    ],
  },
  {
    key: '3',
    label: 'Music',
    isLeaf: true,
  },
])

const expandedKeys = ref<(string | number)[]>(['1'])
</script>

<template>
  <div class="w-80">
    <BrTree
      v-model:expanded-keys="expandedKeys"
      :data="treeData"
    />
  </div>
</template>

Selection

Use v-model:selectedKeys to control which nodes are selected. Click a node to select it. Set selectable to "multiple" for multi-select or "none" to disable selection.

<script setup lang="ts">
import { ref } from 'vue'
import { BrTree } from '@breezeui/vue'
import type { TreeNodeData } from '@breezeui/vue'

const treeData = ref<TreeNodeData[]>([
  {
    key: '1',
    label: 'Documents',
    children: [
      {
        key: '1-1',
        label: 'Work',
        children: [
          { key: '1-1-1', label: 'project.pdf' },
          { key: '1-1-2', label: 'report.docx' },
        ],
      },
      {
        key: '1-2',
        label: 'Personal',
        children: [
          { key: '1-2-1', label: 'resume.pdf' },
          { key: '1-2-2', label: 'budget.xlsx' },
        ],
      },
    ],
  },
  {
    key: '2',
    label: 'Pictures',
    children: [
      { key: '2-1', label: 'vacation.jpg' },
      { key: '2-2', label: 'family.png' },
    ],
  },
])

const expandedKeys = ref<(string | number)[]>(['1'])
const selectedKeys = ref<(string | number)[]>([])

function onSelect(keys: (string | number)[], node: TreeNodeData) {
  console.log('Selected:', keys, node)
}
</script>

<template>
  <div class="w-80">
    <BrTree
      v-model:expanded-keys="expandedKeys"
      v-model:selected-keys="selectedKeys"
      :data="treeData"
      @select="onSelect"
    />
  </div>
</template>

Checkable

Set checkable to enable checkboxes. Use v-model:checkedKeys to control checked state. Parent-child linkage is built-in: checking a parent checks all children, and checking all children checks the parent.

<script setup lang="ts">
import { ref } from 'vue'
import { BrTree } from '@breezeui/vue'
import type { TreeNodeData } from '@breezeui/vue'

const treeData = ref<TreeNodeData[]>([
  {
    key: '1',
    label: 'All Files',
    children: [
      {
        key: '1-1',
        label: 'Documents',
        children: [
          { key: '1-1-1', label: 'project.pdf' },
          { key: '1-1-2', label: 'report.docx' },
          { key: '1-1-3', label: 'notes.txt' },
        ],
      },
      {
        key: '1-2',
        label: 'Pictures',
        children: [
          { key: '1-2-1', label: 'vacation.jpg' },
          { key: '1-2-2', label: 'family.png' },
        ],
      },
      {
        key: '1-3',
        label: 'Music',
        children: [
          { key: '1-3-1', label: 'song.mp3' },
        ],
      },
    ],
  },
])

const expandedKeys = ref<(string | number)[]>(['1', '1-1'])
const checkedKeys = ref<(string | number)[]>(['1-1-1', '1-1-2'])

function onCheck(keys: (string | number)[], node: TreeNodeData) {
  console.log('Checked:', keys, node)
}
</script>

<template>
  <div class="w-80">
    <BrTree
      v-model:expanded-keys="expandedKeys"
      v-model:checked-keys="checkedKeys"
      :data="treeData"
      checkable
      @check="onCheck"
    />
  </div>
</template>

Disabled & Show Line

Set disabled on individual nodes or the entire tree. Use showLine to display connecting lines between levels.

<script setup lang="ts">
import { ref } from 'vue'
import { BrTree } from '@breezeui/vue'
import type { TreeNodeData } from '@breezeui/vue'

const treeData = ref<TreeNodeData[]>([
  {
    key: '1',
    label: 'Documents',
    children: [
      {
        key: '1-1',
        label: 'Work',
        children: [
          { key: '1-1-1', label: 'project.pdf' },
          { key: '1-1-2', label: 'report.docx' },
        ],
      },
      {
        key: '1-2',
        label: 'Personal',
        children: [
          { key: '1-2-1', label: 'resume.pdf' },
        ],
      },
    ],
  },
  {
    key: '2',
    label: 'Disabled Folder',
    disabled: true,
    children: [
      { key: '2-1', label: 'locked-file.txt' },
    ],
  },
  {
    key: '3',
    label: 'Pictures',
    children: [
      { key: '3-1', label: 'vacation.jpg' },
      { key: '3-2', label: 'disabled.png', disabled: true },
    ],
  },
])

const expandedKeys = ref<(string | number)[]>(['1'])
</script>

<template>
  <div class="w-80">
    <BrTree
      v-model:expanded-keys="expandedKeys"
      :data="treeData"
      show-line
      default-expand-all
    />
  </div>
</template>

Filter

Use filterTreeNode to filter visible nodes. Pass a function that returns true for matching nodes. Matching nodes and their ancestors will be shown automatically.

<script setup lang="ts">
import { ref, computed } from 'vue'
import { BrTree, BrInput } from '@breezeui/vue'
import type { TreeNodeData } from '@breezeui/vue'

const treeData = ref<TreeNodeData[]>([
  {
    key: '1',
    label: 'Documents',
    children: [
      {
        key: '1-1',
        label: 'Work',
        children: [
          { key: '1-1-1', label: 'project.pdf' },
          { key: '1-1-2', label: 'report.docx' },
          { key: '1-1-3', label: 'presentation.pptx' },
        ],
      },
      {
        key: '1-2',
        label: 'Personal',
        children: [
          { key: '1-2-1', label: 'resume.pdf' },
          { key: '1-2-2', label: 'budget.xlsx' },
        ],
      },
    ],
  },
  {
    key: '2',
    label: 'Pictures',
    children: [
      {
        key: '2-1',
        label: 'Vacation',
        children: [
          { key: '2-1-1', label: 'beach.jpg' },
          { key: '2-1-2', label: 'sunset.jpg' },
        ],
      },
      { key: '2-2', label: 'family.png' },
    ],
  },
  {
    key: '3',
    label: 'Music',
    isLeaf: true,
  },
])

const searchQuery = ref('')
const filterFn = computed(() => {
  if (!searchQuery.value.trim()) return undefined
  const q = searchQuery.value.toLowerCase()
  return (node: TreeNodeData) =>
    node.label.toLowerCase().includes(q)
})
</script>

<template>
  <div class="space-y-3">
    <div class="w-72">
      <BrInput v-model="searchQuery" placeholder="Type to filter nodes..." size="sm" />
    </div>
    <div class="w-80">
      <BrTree
        :data="treeData"
        :filter-tree-node="filterFn"
        default-expand-all
        show-icon
      />
    </div>
  </div>
</template>

Virtual Scroll

For large datasets, enable virtualScroll to render only visible rows. Set height to control the container height and itemHeight for each row height.

<script setup lang="ts">
import { ref } from 'vue'
import { BrTree } from '@breezeui/vue'
import type { TreeNodeData } from '@breezeui/vue'

function generateLargeData(count: number, depth = 0, prefix = ''): TreeNodeData[] {
  const result: TreeNodeData[] = []
  for (let i = 0; i < count; i++) {
    const key = `${prefix}${i}`
    if (depth < 3) {
      result.push({
        key,
        label: `Node ${key}`,
        children: generateLargeData(5, depth + 1, `${key}-`),
      })
    } else {
      result.push({ key, label: `Leaf ${key}`, isLeaf: true })
    }
  }
  return result
}

const virtualData = ref<TreeNodeData[]>(generateLargeData(20))
</script>

<template>
  <div class="w-96">
    <BrTree
      :data="virtualData"
      virtual-scroll
      :item-height="32"
      :height="400"
      default-expand-all
      show-icon
    />
  </div>
</template>

Drag & Drop

Set draggable to enable drag-and-drop reordering. Listen to drop event to handle the new position. Use allowDrop to restrict valid drop targets.

<script setup lang="ts">
import { ref } from 'vue'
import { BrTree } from '@breezeui/vue'
import type { TreeNodeData, DropInfo } from '@breezeui/vue'

const treeData = ref<TreeNodeData[]>([
  {
    key: 'd1',
    label: 'Root A',
    children: [
      { key: 'd1-1', label: 'Child A1' },
      { key: 'd1-2', label: 'Child A2' },
      { key: 'd1-3', label: 'Child A3' },
    ],
  },
  {
    key: 'd2',
    label: 'Root B',
    children: [
      { key: 'd2-1', label: 'Child B1' },
      { key: 'd2-2', label: 'Child B2' },
    ],
  },
  {
    key: 'd3',
    label: 'Root C',
    children: [
      { key: 'd3-1', label: 'Child C1' },
    ],
  },
])

const expandedKeys = ref<(string | number)[]>(['d1', 'd2', 'd3'])

function handleDrop(info: DropInfo) {
  console.log('Drop:', info)
}
</script>

<template>
  <div class="w-80">
    <BrTree
      v-model:expanded-keys="expandedKeys"
      :data="treeData"
      draggable
      show-icon
      @drop="handleDrop"
    />
  </div>
</template>

Theming

You can customize the appearance using Tailwind CSS classes or by overriding CSS variables.

CSS Variables

:root {
  --background: 0 0% 100%;
  --foreground: 222.2 84% 4.9%;
  --border: 214.3 31.8% 91.4%;
  --radius: 0.5rem;
}

Tailwind Customization

Use the class prop to add custom styles:

<BrTree class="border rounded-md p-2" :data="treeData" />

API

BrTree Props

PropTypeDefaultDescription
dataTreeNodeData[][]Tree data source.
expandedKeys(string | number)[]-Expanded node keys (controlled, v-model:expandedKeys).
selectedKeys(string | number)[]-Selected node keys (controlled, v-model:selectedKeys).
checkedKeys(string | number)[]-Checked node keys (controlled, v-model:checkedKeys).
checkablebooleanfalseWhether to show checkboxes.
selectableboolean | 'single' | 'multiple' | 'none'trueSelection mode.
showLinebooleanfalseWhether to show connecting lines.
showIconbooleantrueWhether to show node icons.
defaultExpandAllbooleanfalseExpand all nodes on mount.
defaultExpandedKeys(string | number)[][]Initially expanded keys (uncontrolled).
defaultSelectedKeys(string | number)[][]Initially selected keys (uncontrolled).
defaultCheckedKeys(string | number)[][]Initially checked keys (uncontrolled).
disabledbooleanfalseDisable the entire tree.
draggablebooleanfalseWhether nodes are draggable.
loadData(node: TreeNodeData) => Promise<TreeNodeData[]>-Async load children callback.
filterTreeNode(node: TreeNodeData) => boolean-Filter function for nodes.
size'xs' | 'sm' | 'default' | 'md' | 'lg' | '2xl''default'Component size.
indentnumber24Indent per level in pixels.
virtualScrollbooleanfalseEnable virtual scrolling for large datasets.
itemHeightnumber32Row height in pixels (virtual scroll mode).
heightnumber | string400Container height (virtual scroll mode).
allowDrop(options: AllowDropOptions) => boolean-Callback to control allowed drop positions.

TreeNodeData

PropertyTypeDescription
keystring | numberUnique identifier.
labelstringDisplay text.
childrenTreeNodeData[]Child nodes.
disabledbooleanDisable this node.
isLeafbooleanForce leaf node (no expand).
iconComponentCustom icon component.
loadingbooleanLoading state.

Events

NamePayloadDescription
update:expandedKeys(string | number)[]Expanded keys changed.
update:selectedKeys(string | number)[]Selected keys changed.
update:checkedKeys(string | number)[]Checked keys changed.
expand(keys, node)Node expanded/collapsed.
select(keys, node)Node selected.
check(keys, node)Node checked/unchecked.
dragStart(node, event)Drag started.
dragEnd(node, event)Drag ended.
drop(info: DropInfo)Node dropped. DropInfo contains { dragNode, dropNode, dropPosition }.

Slots

NamePropsDescription
switcher{ expanded, node }Custom expand/collapse icon.
icon{ node, expanded }Custom node icon.
label{ node }Custom node content.