Tree Component
Tree component displays a set of data with hierarchies, supporting selection, filtering, lazy loading, and drag-and-drop functionality.
When to Use
- Display folder or directory structures
- Show organization charts
- Category or classification selection
- Menu navigation with nested items
- Any hierarchical data representation
Basic Usage
<template>
<el-tree :data="data" :props="defaultProps" />
</template>
<script setup>
const data = [
{
label: 'Level one 1',
children: [
{
label: 'Level two 1-1',
children: [
{ label: 'Level three 1-1-1' }
]
}
]
},
{
label: 'Level one 2',
children: [
{ label: 'Level two 2-1' },
{ label: 'Level two 2-2' }
]
}
]
const defaultProps = {
children: 'children',
label: 'label'
}
</script>
Selectable Tree with Checkboxes
<template>
<el-tree
ref="treeRef"
:data="data"
:props="defaultProps"
show-checkbox
node-key="id"
:default-expanded-keys="[2, 3]"
:default-checked-keys="[5]"
/>
</template>
<script setup>
import { ref } from 'vue'
const treeRef = ref()
const data = [
{
id: 1,
label: 'Level one 1',
children: [
{ id: 4, label: 'Level two 1-1' },
{ id: 5, label: 'Level two 1-2' }
]
},
{
id: 2,
label: 'Level one 2',
children: [
{ id: 6, label: 'Level two 2-1' }
]
},
{
id: 3,
label: 'Level one 3',
children: [
{ id: 7, label: 'Level two 3-1' }
]
}
]
const defaultProps = {
children: 'children',
label: 'label'
}
</script>
Lazy Loading
<template>
<el-tree
:props="props"
:load="loadNode"
lazy
show-checkbox
/>
</template>
<script setup>
const props = {
label: 'name',
children: 'zones',
isLeaf: 'leaf'
}
const loadNode = (node, resolve) => {
if (node.level === 0) {
return resolve([{ name: 'Root' }])
}
if (node.level > 1) {
return resolve([])
}
setTimeout(() => {
const data = [
{ name: 'leaf', leaf: true },
{ name: 'zone' }
]
resolve(data)
}, 500)
}
</script>
Custom Node Content
<template>
<el-tree :data="data" :props="defaultProps">
<template #default="{ node, data }">
<span class="custom-tree-node">
<span>{{ node.label }}</span>
<span>
<el-button type="primary" size="small" @click="append(data)">
Append
</el-button>
<el-button type="danger" size="small" @click="remove(node, data)">
Delete
</el-button>
</span>
</span>
</template>
</el-tree>
</template>
<script setup>
let id = 1000
const append = (data) => {
const newChild = { label: 'test', children: [] }
if (!data.children) {
data.children = []
}
data.children.push(newChild)
}
const remove = (node, data) => {
const parent = node.parent
const children = parent.data.children || parent.data
const index = children.findIndex(d => d.id === data.id)
children.splice(index, 1)
}
</script>
Filtering
<template>
<el-input
v-model="filterText"
placeholder="Filter keyword"
/>
<el-tree
ref="treeRef"
:data="data"
:props="defaultProps"
:filter-node-method="filterNode"
/>
</template>
<script setup>
import { ref, watch } from 'vue'
const filterText = ref('')
const treeRef = ref()
const filterNode = (value, data) => {
if (!value) return true
return data.label.includes(value)
}
watch(filterText, (val) => {
treeRef.value.filter(val)
})
</script>
Accordion Mode
<template>
<el-tree
:data="data"
:props="defaultProps"
accordion
/>
</template>
Draggable Tree
<template>
<el-tree
:data="data"
:props="defaultProps"
draggable
:allow-drop="allowDrop"
:allow-drag="allowDrag"
@node-drag-start="handleDragStart"
@node-drag-enter="handleDragEnter"
@node-drag-leave="handleDragLeave"
@node-drag-over="handleDragOver"
@node-drag-end="handleDragEnd"
@node-drop="handleDrop"
/>
</template>
<script setup>
const allowDrop = (draggingNode, dropNode, type) => {
if (dropNode.data.label.includes('Level three')) {
return type !== 'inner'
}
return true
}
const allowDrag = (draggingNode) => {
return !draggingNode.data.label.includes('Level three')
}
const handleDragStart = (node, ev) => {
console.log('drag start', node)
}
const handleDrop = (draggingNode, dropNode, dropType, ev) => {
console.log('drop', draggingNode, dropNode, dropType)
}
</script>
API Reference
Attributes
| Name |
Description |
Type |
Default |
| data |
Tree data |
Array<{[key: string]: any}> |
— |
| empty-text |
Text when data is empty |
string |
— |
| node-key |
Unique identity key for nodes |
string |
— |
| props |
Configuration options |
object |
— |
| render-after-expand |
Render children only after parent expanded |
boolean |
true |
| load |
Method for loading subtree data (lazy mode) |
(node, resolve, reject) => void |
— |
| render-content |
Render function for tree node |
(h, { node, data, store }) => void |
— |
| highlight-current |
Highlight current node |
boolean |
false |
| default-expand-all |
Expand all nodes by default |
boolean |
false |
| expand-on-click-node |
Expand on node click |
boolean |
true |
| check-on-click-node |
Check on node click |
boolean |
false |
| check-on-click-leaf |
Check on leaf node click |
boolean |
true |
| auto-expand-parent |
Expand parent when child expands |
boolean |
true |
| default-expanded-keys |
Keys of initially expanded nodes |
Array<string | number> |
— |
| show-checkbox |
Show checkboxes |
boolean |
false |
| check-strictly |
Parent-child selection independent |
boolean |
false |
| default-checked-keys |
Keys of initially checked nodes |
Array<string | number> |
— |
| current-node-key |
Key of initially selected node |
string | number |
— |
| filter-node-method |
Filter method |
(value, data, node) => boolean |
— |
| accordion |
Only one sibling expanded at a time |
boolean |
false |
| indent |
Indentation in pixels |
number |
18 |
| icon |
Custom tree node icon |
string | Component |
— |
| lazy |
Enable lazy loading |
boolean |
false |
| draggable |
Enable drag and drop |
boolean |
false |
| allow-drag |
Function to determine if draggable |
(node) => boolean |
— |
| allow-drop |
Function to determine if droppable |
(draggingNode, dropNode, type) => boolean |
— |
Props Configuration
| Attribute |
Description |
Type |
Default |
| label |
Key for node label |
string | (data, node) => string |
— |
| children |
Key for node subtree |
string |
— |
| disabled |
Key for disabled state |
string | (data, node) => boolean |
— |
| isLeaf |
Key for leaf node detection |
string | (data, node) => boolean |
— |
| class |
Custom node class |
string | (data, node) => string |
— |
Exposes (Methods)
| Method |
Description |
Parameters |
| filter |
Filter all tree nodes |
(value) |
| updateKeyChildren |
Set new data to node |
(key, data) |
| getCheckedNodes |
Get checked nodes |
(leafOnly, includeHalfChecked) |
| setCheckedNodes |
Set checked nodes |
(nodes, leafOnly) |
| getCheckedKeys |
Get checked keys |
(leafOnly) |
| setCheckedKeys |
Set checked keys |
(keys, leafOnly) |
| setChecked |
Set node checked state |
(key/data, checked, deep) |
| getHalfCheckedNodes |
Get half-checked nodes |
— |
| getHalfCheckedKeys |
Get half-checked keys |
— |
| getCurrentKey |
Get current node key |
— |
| getCurrentNode |
Get current node data |
— |
| setCurrentKey |
Set current node by key |
(key, shouldAutoExpandParent) |
| setCurrentNode |
Set current node |
(node, shouldAutoExpandParent) |
| getNode |
Get node by data or key |
(data) |
| remove |
Remove a node |
(data) |
| append |
Append child node |
(data, parentNode) |
| insertBefore |
Insert node before another |
(data, refNode) |
| insertAfter |
Insert node after another |
(data, refNode) |
Events
| Name |
Description |
Parameters |
| node-click |
Node clicked |
(data, node, TreeNode, event) |
| node-contextmenu |
Right-click on node |
(event, data, node, TreeNode) |
| check-change |
Selection state changed |
(data, checked, indeterminate) |
| check |
Checkbox clicked |
(data, { checkedNodes, checkedKeys, halfCheckedNodes, halfCheckedKeys }) |
| current-change |
Current node changed |
(data, node) |
| node-expand |
Node expanded |
(data, node, TreeNode) |
| node-collapse |
Node collapsed |
(data, node, TreeNode) |
| node-drag-start |
Drag started |
(node, event) |
| node-drag-enter |
Drag enters node |
(draggingNode, enterNode, event) |
| node-drag-leave |
Drag leaves node |
(draggingNode, leaveNode, event) |
| node-drag-over |
Drag over node |
(draggingNode, overNode, event) |
| node-drag-end |
Drag ended |
(draggingNode, endNode, type, event) |
| node-drop |
Node dropped |
(draggingNode, dropNode, type, event) |
Slots
| Name |
Description |
| default |
Custom content for tree nodes |
| empty |
Custom content when data is empty |
Common Patterns
Get Selected Nodes
<template>
<el-tree
ref="treeRef"
:data="data"
show-checkbox
node-key="id"
/>
<el-button @click="getChecked">Get Checked</el-button>
</template>
<script setup>
import { ref } from 'vue'
const treeRef = ref()
const getChecked = () => {
const checkedNodes = treeRef.value.getCheckedNodes()
const checkedKeys = treeRef.value.getCheckedKeys()
console.log('Nodes:', checkedNodes)
console.log('Keys:', checkedKeys)
}
</script>
Dynamic Tree Operations
<template>
<el-tree
ref="treeRef"
:data="data"
node-key="id"
default-expand-all
/>
<el-button @click="addNode">Add Node</el-button>
<el-button @click="removeNode">Remove Node</el-button>
</template>
<script setup>
import { ref } from 'vue'
const treeRef = ref()
const data = ref([
{ id: 1, label: 'Root', children: [] }
])
const addNode = () => {
const newNode = { id: Date.now(), label: 'New Node', children: [] }
treeRef.value.append(newNode, data.value[0])
}
const removeNode = () => {
const node = treeRef.value.getNode(data.value[0].children[0])
if (node) {
treeRef.value.remove(node)
}
}
</script>
Component Interactions
- Use with Form for hierarchical data selection in forms
- Combine with Input for tree filtering
- Use with Dialog for tree selection in modals
- Integrate with Menu for nested navigation structures
Best Practices
- Always set
node-key when using selection features
- Use
lazy loading for large datasets to improve performance
- Implement
filter-node-method for searchable trees
- Use
check-strictly when parent-child selection should be independent
- Consider
accordion mode for space-constrained layouts
1---2name: el-tree3description: Tree component for displaying hierarchical data with selection, filtering, and drag-drop support. Invoke when user needs to display folder structures, organization charts, category trees, or any hierarchical data with expandable nodes.4---5
6# Tree Component
7
8Tree component displays a set of data with hierarchies, supporting selection, filtering, lazy loading, and drag-and-drop functionality.
9
10## When to Use
11
12- Display folder or directory structures
13- Show organization charts
14- Category or classification selection
15- Menu navigation with nested items
16- Any hierarchical data representation
17
18## Basic Usage
19
20```vue
21<template>
22 <el-tree :data="data" :props="defaultProps" />
23</template>
24
25<script setup>
26const data = [
27 {
28 label: 'Level one 1',
29 children: [
30 {
31 label: 'Level two 1-1',
32 children: [
33 { label: 'Level three 1-1-1' }
34 ]
35 }
36 ]
37 },
38 {
39 label: 'Level one 2',
40 children: [
41 { label: 'Level two 2-1' },
42 { label: 'Level two 2-2' }
43 ]
44 }
45]
46
47const defaultProps = {
48 children: 'children',
49 label: 'label'
50}
51</script>
52```
53
54## Selectable Tree with Checkboxes
55
56```vue
57<template>
58 <el-tree
59 ref="treeRef"
60 :data="data"
61 :props="defaultProps"
62 show-checkbox
63 node-key="id"
64 :default-expanded-keys="[2, 3]"
65 :default-checked-keys="[5]"
66 />
67</template>
68
69<script setup>
70import { ref } from 'vue'
71
72const treeRef = ref()
73
74const data = [
75 {
76 id: 1,
77 label: 'Level one 1',
78 children: [
79 { id: 4, label: 'Level two 1-1' },
80 { id: 5, label: 'Level two 1-2' }
81 ]
82 },
83 {
84 id: 2,
85 label: 'Level one 2',
86 children: [
87 { id: 6, label: 'Level two 2-1' }
88 ]
89 },
90 {
91 id: 3,
92 label: 'Level one 3',
93 children: [
94 { id: 7, label: 'Level two 3-1' }
95 ]
96 }
97]
98
99const defaultProps = {
100 children: 'children',
101 label: 'label'
102}
103</script>
104```
105
106## Lazy Loading
107
108```vue
109<template>
110 <el-tree
111 :props="props"
112 :load="loadNode"
113 lazy
114 show-checkbox
115 />
116</template>
117
118<script setup>
119const props = {
120 label: 'name',
121 children: 'zones',
122 isLeaf: 'leaf'
123}
124
125const loadNode = (node, resolve) => {
126 if (node.level === 0) {
127 return resolve([{ name: 'Root' }])
128 }
129
130 if (node.level > 1) {
131 return resolve([])
132 }
133
134 setTimeout(() => {
135 const data = [
136 { name: 'leaf', leaf: true },
137 { name: 'zone' }
138 ]
139 resolve(data)
140 }, 500)
141}
142</script>
143```
144
145## Custom Node Content
146
147```vue
148<template>
149 <el-tree :data="data" :props="defaultProps">
150 <template #default="{ node, data }">
151 <span class="custom-tree-node">
152 <span>{{ node.label }}</span>
153 <span>
154 <el-button type="primary" size="small" @click="append(data)">
155 Append
156 </el-button>
157 <el-button type="danger" size="small" @click="remove(node, data)">
158 Delete
159 </el-button>
160 </span>
161 </span>
162 </template>
163 </el-tree>
164</template>
165
166<script setup>
167let id = 1000
168
169const append = (data) => {
170 const newChild = { label: 'test', children: [] }
171 if (!data.children) {
172 data.children = []
173 }
174 data.children.push(newChild)
175}
176
177const remove = (node, data) => {
178 const parent = node.parent
179 const children = parent.data.children || parent.data
180 const index = children.findIndex(d => d.id === data.id)
181 children.splice(index, 1)
182}
183</script>
184```
185
186## Filtering
187
188```vue
189<template>
190 <el-input
191 v-model="filterText"
192 placeholder="Filter keyword"
193 />
194 <el-tree
195 ref="treeRef"
196 :data="data"
197 :props="defaultProps"
198 :filter-node-method="filterNode"
199 />
200</template>
201
202<script setup>
203import { ref, watch } from 'vue'
204
205const filterText = ref('')
206const treeRef = ref()
207
208const filterNode = (value, data) => {
209 if (!value) return true
210 return data.label.includes(value)
211}
212
213watch(filterText, (val) => {
214 treeRef.value.filter(val)
215})
216</script>
217```
218
219## Accordion Mode
220
221```vue
222<template>
223 <el-tree
224 :data="data"
225 :props="defaultProps"
226 accordion
227 />
228</template>
229```
230
231## Draggable Tree
232
233```vue
234<template>
235 <el-tree
236 :data="data"
237 :props="defaultProps"
238 draggable
239 :allow-drop="allowDrop"
240 :allow-drag="allowDrag"
241 @node-drag-start="handleDragStart"
242 @node-drag-enter="handleDragEnter"
243 @node-drag-leave="handleDragLeave"
244 @node-drag-over="handleDragOver"
245 @node-drag-end="handleDragEnd"
246 @node-drop="handleDrop"
247 />
248</template>
249
250<script setup>
251const allowDrop = (draggingNode, dropNode, type) => {
252 if (dropNode.data.label.includes('Level three')) {
253 return type !== 'inner'
254 }
255 return true
256}
257
258const allowDrag = (draggingNode) => {
259 return !draggingNode.data.label.includes('Level three')
260}
261
262const handleDragStart = (node, ev) => {
263 console.log('drag start', node)
264}
265
266const handleDrop = (draggingNode, dropNode, dropType, ev) => {
267 console.log('drop', draggingNode, dropNode, dropType)
268}
269</script>
270```
271
272## API Reference
273
274### Attributes
275
276| Name | Description | Type | Default |
277|------|-------------|------|---------|
278| data | Tree data | `Array<{[key: string]: any}>` | — |
279| empty-text | Text when data is empty | `string` | — |
280| node-key | Unique identity key for nodes | `string` | — |
281| props | Configuration options | `object` | — |
282| render-after-expand | Render children only after parent expanded | `boolean` | `true` |
283| load | Method for loading subtree data (lazy mode) | `(node, resolve, reject) => void` | — |
284| render-content | Render function for tree node | `(h, { node, data, store }) => void` | — |
285| highlight-current | Highlight current node | `boolean` | `false` |
286| default-expand-all | Expand all nodes by default | `boolean` | `false` |
287| expand-on-click-node | Expand on node click | `boolean` | `true` |
288| check-on-click-node | Check on node click | `boolean` | `false` |
289| check-on-click-leaf | Check on leaf node click | `boolean` | `true` |
290| auto-expand-parent | Expand parent when child expands | `boolean` | `true` |
291| default-expanded-keys | Keys of initially expanded nodes | `Array<string \| number>` | — |
292| show-checkbox | Show checkboxes | `boolean` | `false` |
293| check-strictly | Parent-child selection independent | `boolean` | `false` |
294| default-checked-keys | Keys of initially checked nodes | `Array<string \| number>` | — |
295| current-node-key | Key of initially selected node | `string \| number` | — |
296| filter-node-method | Filter method | `(value, data, node) => boolean` | — |
297| accordion | Only one sibling expanded at a time | `boolean` | `false` |
298| indent | Indentation in pixels | `number` | `18` |
299| icon | Custom tree node icon | `string \| Component` | — |
300| lazy | Enable lazy loading | `boolean` | `false` |
301| draggable | Enable drag and drop | `boolean` | `false` |
302| allow-drag | Function to determine if draggable | `(node) => boolean` | — |
303| allow-drop | Function to determine if droppable | `(draggingNode, dropNode, type) => boolean` | — |
304
305### Props Configuration
306
307| Attribute | Description | Type | Default |
308|-----------|-------------|------|---------|
309| label | Key for node label | `string \| (data, node) => string` | — |
310| children | Key for node subtree | `string` | — |
311| disabled | Key for disabled state | `string \| (data, node) => boolean` | — |
312| isLeaf | Key for leaf node detection | `string \| (data, node) => boolean` | — |
313| class | Custom node class | `string \| (data, node) => string` | — |
314
315### Exposes (Methods)
316
317| Method | Description | Parameters |
318|--------|-------------|------------|
319| filter | Filter all tree nodes | `(value)` |
320| updateKeyChildren | Set new data to node | `(key, data)` |
321| getCheckedNodes | Get checked nodes | `(leafOnly, includeHalfChecked)` |
322| setCheckedNodes | Set checked nodes | `(nodes, leafOnly)` |
323| getCheckedKeys | Get checked keys | `(leafOnly)` |
324| setCheckedKeys | Set checked keys | `(keys, leafOnly)` |
325| setChecked | Set node checked state | `(key/data, checked, deep)` |
326| getHalfCheckedNodes | Get half-checked nodes | — |
327| getHalfCheckedKeys | Get half-checked keys | — |
328| getCurrentKey | Get current node key | — |
329| getCurrentNode | Get current node data | — |
330| setCurrentKey | Set current node by key | `(key, shouldAutoExpandParent)` |
331| setCurrentNode | Set current node | `(node, shouldAutoExpandParent)` |
332| getNode | Get node by data or key | `(data)` |
333| remove | Remove a node | `(data)` |
334| append | Append child node | `(data, parentNode)` |
335| insertBefore | Insert node before another | `(data, refNode)` |
336| insertAfter | Insert node after another | `(data, refNode)` |
337
338### Events
339
340| Name | Description | Parameters |
341|------|-------------|------------|
342| node-click | Node clicked | `(data, node, TreeNode, event)` |
343| node-contextmenu | Right-click on node | `(event, data, node, TreeNode)` |
344| check-change | Selection state changed | `(data, checked, indeterminate)` |
345| check | Checkbox clicked | `(data, { checkedNodes, checkedKeys, halfCheckedNodes, halfCheckedKeys })` |
346| current-change | Current node changed | `(data, node)` |
347| node-expand | Node expanded | `(data, node, TreeNode)` |
348| node-collapse | Node collapsed | `(data, node, TreeNode)` |
349| node-drag-start | Drag started | `(node, event)` |
350| node-drag-enter | Drag enters node | `(draggingNode, enterNode, event)` |
351| node-drag-leave | Drag leaves node | `(draggingNode, leaveNode, event)` |
352| node-drag-over | Drag over node | `(draggingNode, overNode, event)` |
353| node-drag-end | Drag ended | `(draggingNode, endNode, type, event)` |
354| node-drop | Node dropped | `(draggingNode, dropNode, type, event)` |
355
356### Slots
357
358| Name | Description |
359|------|-------------|
360| default | Custom content for tree nodes |
361| empty | Custom content when data is empty |
362
363## Common Patterns
364
365### Get Selected Nodes
366
367```vue
368<template>
369 <el-tree
370 ref="treeRef"
371 :data="data"
372 show-checkbox
373 node-key="id"
374 />
375 <el-button @click="getChecked">Get Checked</el-button>
376</template>
377
378<script setup>
379import { ref } from 'vue'
380
381const treeRef = ref()
382
383const getChecked = () => {
384 const checkedNodes = treeRef.value.getCheckedNodes()
385 const checkedKeys = treeRef.value.getCheckedKeys()
386 console.log('Nodes:', checkedNodes)
387 console.log('Keys:', checkedKeys)
388}
389</script>
390```
391
392### Dynamic Tree Operations
393
394```vue
395<template>
396 <el-tree
397 ref="treeRef"
398 :data="data"
399 node-key="id"
400 default-expand-all
401 />
402 <el-button @click="addNode">Add Node</el-button>
403 <el-button @click="removeNode">Remove Node</el-button>
404</template>
405
406<script setup>
407import { ref } from 'vue'
408
409const treeRef = ref()
410const data = ref([
411 { id: 1, label: 'Root', children: [] }
412])
413
414const addNode = () => {
415 const newNode = { id: Date.now(), label: 'New Node', children: [] }
416 treeRef.value.append(newNode, data.value[0])
417}
418
419const removeNode = () => {
420 const node = treeRef.value.getNode(data.value[0].children[0])
421 if (node) {
422 treeRef.value.remove(node)
423 }
424}
425</script>
426```
427
428## Component Interactions
429
430- Use with **Form** for hierarchical data selection in forms
431- Combine with **Input** for tree filtering
432- Use with **Dialog** for tree selection in modals
433- Integrate with **Menu** for nested navigation structures
434
435## Best Practices
436
4371. Always set `node-key` when using selection features
4382. Use `lazy` loading for large datasets to improve performance
4393. Implement `filter-node-method` for searchable trees
4404. Use `check-strictly` when parent-child selection should be independent
4415. Consider `accordion` mode for space-constrained layouts