Back to skills
SKILL.md
N Tree
ASecurityA powerful tree component for displaying hierarchical data with features like selection, checking, drag-and-drop, virtual scrolling, async loading, and search filtering.
- 8 stars
- 0 votes
- 0 copies
- 2 views
- Added September 11, 2026
Works with
Security analysis
100/100npx -y skills add jiaiyan/naive-ui-skills --skill n-tree --agent claude-codeAre you the author of N Tree?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/jiaiyan-n-tree)---
name: n-tree
description: A powerful tree component for displaying hierarchical data with features like selection, checking, drag-and-drop, virtual scrolling, async loading, and search filtering.
author: jiaiyan
version: 1.0.0
---
# n-tree Component
The `n-tree` component is Naive UI's comprehensive solution for displaying hierarchical data structures. It provides extensive features for tree selection, checking, searching, and manipulation.
## When to Use
Use `n-tree` when you need to:
- Display hierarchical data structures
- Create file/folder browsers
- Build organization charts
- Implement category selectors
- Handle drag-and-drop tree manipulation
- Support async data loading
## Basic Usage
### Simple Tree
```vue
<template>
<n-tree
block-line
:data="data"
:default-expanded-keys="defaultExpandedKeys"
selectable
/>
</template>
<script setup>
import { ref } from 'vue'
const data = ref([
{
key: '1',
label: 'Root Node',
children: [
{ key: '1-1', label: 'Child 1' },
{ key: '1-2', label: 'Child 2' }
]
}
])
const defaultExpandedKeys = ref(['1'])
</script>
```
### Tree with Checkboxes
```vue
<template>
<n-tree
block-line
checkable
:data="data"
:default-expanded-keys="defaultExpandedKeys"
:default-checked-keys="defaultCheckedKeys"
/>
</template>
<script setup>
const data = [
{
key: '1',
label: 'Parent',
children: [
{ key: '1-1', label: 'Child 1' },
{ key: '1-2', label: 'Child 2' }
]
}
]
const defaultExpandedKeys = ['1']
const defaultCheckedKeys = ['1-1']
</script>
```
### Cascade Selection
```vue
<template>
<n-tree
block-line
cascade
checkable
:data="data"
:default-expanded-keys="defaultExpandedKeys"
/>
</template>
```
### Multiple Selection
```vue
<template>
<n-tree
multiple
block-line
:data="data"
v-model:selected-keys="selectedKeys"
/>
</template>
<script setup>
import { ref } from 'vue'
const selectedKeys = ref([])
</script>
```
### Custom Key and Label Fields
```vue
<template>
<n-tree
block-line
:data="data"
key-field="id"
label-field="name"
children-field="items"
selectable
/>
</template>
<script setup>
const data = [
{
id: '1',
name: 'Custom Key',
items: [
{ id: '1-1', name: 'Child' }
]
}
]
</script>
```
## API Reference
### Props
| Name | Type | Default | Description |
|------|------|---------|-------------|
| `accordion` | `boolean` | `false` | Whether to use accordion expand mode |
| `allow-checking-not-loaded` | `boolean` | `false` | Allow cascade checking on not loaded nodes |
| `allow-drop` | `(info) => boolean` | - | Whether to allow dropping |
| `animated` | `boolean` | `true` | Whether to show expand animation |
| `block-line` | `boolean` | `false` | Nodes spread out the whole row |
| `block-node` | `boolean` | `false` | Node name spreads out the whole row |
| `cancelable` | `boolean` | `true` | Whether node selection can be cancelled |
| `cascade` | `boolean` | `false` | Whether to cascade checkboxes |
| `check-strategy` | `'all' \| 'parent' \| 'child'` | `'all'` | Strategy for checked callback keys |
| `checkable` | `boolean` | `false` | Whether to display checkboxes |
| `checkbox-placement` | `'left' \| 'right'` | `'left'` | Checkbox placement |
| `checked-keys` | `Array<string \| number>` | `undefined` | Checked keys (controlled) |
| `check-on-click` | `boolean \| ((node) => boolean)` | `false` | Allow click to trigger check |
| `data` | `Array<TreeOption>` | `[]` | Tree node data |
| `default-checked-keys` | `Array<string \| number>` | `[]` | Default checked keys |
| `default-expand-all` | `boolean` | `false` | Expand all nodes |
| `default-expanded-keys` | `Array<string \| number>` | `[]` | Default expanded keys |
| `default-selected-keys` | `Array<string \| number>` | `[]` | Default selected keys |
| `disabled-field` | `string` | `'disabled'` | Disabled field name in TreeOption |
| `draggable` | `boolean` | `false` | Whether nodes can be dragged |
| `ellipsis` | `boolean` | `false` | Whether to ellipsis overflow text |
| `expand-on-click` | `boolean` | `false` | Whether to expand on click |
| `expand-on-dragenter` | `boolean` | `true` | Whether to expand on dragenter |
| `expanded-keys` | `Array<string \| number>` | `undefined` | Expanded keys (controlled) |
| `filter` | `(pattern, node) => boolean` | - | Filter function based on pattern |
| `get-children` | `(option) => unknown` | `undefined` | Get children of option |
| `indent` | `number` | `24` | Indent per level |
| `indeterminate-keys` | `Array<string \| number>` | `undefined` | Indeterminate keys |
| `keyboard` | `boolean` | `true` | Whether to support keyboard |
| `key-field` | `string` | `'key'` | Key field in TreeOption |
| `label-field` | `string` | `'label'` | Label field in TreeOption |
| `multiple` | `boolean` | `false` | Whether to allow multiple selection |
| `node-props` | `(info) => HTMLAttributes` | `undefined` | HTML attributes of node |
| `on-load` | `(node) => Promise<void>` | `undefined` | Async load callback |
| `override-default-node-click-behavior` | `(info) => string` | `undefined` | Override default click behavior |
| `pattern` | `string` | `''` | Search pattern |
| `render-label` | `(info) => VNodeChild` | `undefined` | Render function for label |
| `render-prefix` | `(info) => VNodeChild` | `undefined` | Render function for prefix |
| `render-suffix` | `(info) => VNodeChild` | `undefined` | Render function for suffix |
| `render-switcher-icon` | `(props) => VNodeChild` | `undefined` | Render function for switcher icon |
| `scrollbar-props` | `ScrollbarProps` | `undefined` | Scrollbar props |
| `selectable` | `boolean` | `true` | Whether nodes can be selected |
| `selected-keys` | `Array<string \| number>` | `undefined` | Selected keys (controlled) |
| `show-irrelevant-nodes` | `boolean` | `true` | Show unmatched nodes in filter mode |
| `show-line` | `boolean` | `false` | Whether to show connection line |
| `spin-props` | `object` | `undefined` | Loading icon properties |
| `virtual-scroll` | `boolean` | `false` | Whether to enable virtual scroll |
| `watch-props` | `Array<string>` | `undefined` | Props to watch for updates |
### Events
| Name | Parameters | Description |
|------|------------|-------------|
| `update:checked-keys` | `(keys, options, meta)` | Emitted when checked keys change |
| `update:expanded-keys` | `(keys, options, meta)` | Emitted when expanded keys change |
| `update:selected-keys` | `(keys, options, meta)` | Emitted when selected keys change |
| `update:indeterminate-keys` | `(keys, options)` | Emitted when indeterminate keys change |
| `dragstart` | `({ node, event })` | Emitted when drag starts |
| `dragend` | `({ node, event })` | Emitted when drag ends |
| `dragenter` | `({ node, event })` | Emitted on dragenter |
| `dragleave` | `({ node, event })` | Emitted on dragleave |
| `drop` | `({ node, dragNode, dropPosition, event })` | Emitted on drop |
### TreeOption Properties
| Name | Type | Description |
|------|------|-------------|
| `key` | `string \| number` | Unique key of the node |
| `label` | `string` | Display label |
| `checkboxDisabled` | `boolean` | Whether checkbox is disabled |
| `children` | `TreeOption[]` | Child nodes |
| `disabled` | `boolean` | Whether node is disabled |
| `isLeaf` | `boolean` | Whether node is a leaf (for async loading) |
| `prefix` | `string \| (() => VNodeChild)` | Prefix content |
| `suffix` | `string \| (() => VNodeChild)` | Suffix content |
### Slots
| Name | Parameters | Description |
|------|------------|-------------|
| `empty` | `()` | Empty state slot |
### Methods
| Name | Parameters | Description |
|------|------------|-------------|
| `scrollTo` | `ScrollTo` | Scroll to a node in virtual scroll mode |
| `getCheckedData` | `()` | Get checked data |
| `getIndeterminateData` | `()` | Get indeterminate data |
## Common Patterns
### Search/Filter
```vue
<template>
<n-space vertical>
<n-input v-model:value="pattern" placeholder="Search" />
<n-tree
:pattern="pattern"
:data="data"
block-line
/>
</n-space>
</template>
<script setup>
import { ref } from 'vue'
const pattern = ref('')
</script>
```
### Async Loading
```vue
<template>
<n-tree
block-line
:data="data"
:on-load="handleLoad"
:expanded-keys="expandedKeys"
@update:expanded-keys="handleExpandedChange"
/>
</template>
<script setup>
import { ref } from 'vue'
const data = ref([
{ key: '1', label: 'Root', isLeaf: false }
])
const expandedKeys = ref([])
const handleLoad = async (node) => {
return new Promise((resolve) => {
setTimeout(() => {
node.children = [
{ key: `${node.key}-1`, label: 'Loaded Child' }
]
resolve()
}, 1000)
})
}
const handleExpandedChange = (keys) => {
expandedKeys.value = keys
}
</script>
```
### Drag and Drop
```vue
<template>
<n-tree
block-line
draggable
:data="data"
@drop="handleDrop"
/>
</template>
<script setup>
import { ref } from 'vue'
const data = ref([
{ key: '1', label: 'Node 1', children: [] },
{ key: '2', label: 'Node 2', children: [] }
])
const handleDrop = ({ node, dragNode, dropPosition }) => {
// Handle drop logic
}
</script>
```
### Virtual Scroll for Large Data
```vue
<template>
<n-tree
block-line
:data="largeData"
default-expand-all
virtual-scroll
style="height: 400px"
/>
</template>
<script setup>
const largeData = Array.from({ length: 1000 }, (_, i) => ({
key: `${i}`,
label: `Node ${i}`,
children: Array.from({ length: 5 }, (_, j) => ({
key: `${i}-${j}`,
label: `Child ${i}-${j}`
}))
}))
</script>
```
### Custom Render Functions
```vue
<template>
<n-tree
block-line
:data="data"
:render-prefix="renderPrefix"
:render-label="renderLabel"
:render-suffix="renderSuffix"
/>
</template>
<script setup>
import { h } from 'vue'
import { NIcon, NButton } from 'naive-ui'
const renderPrefix = ({ option }) => {
return h(NIcon, null, { default: () => 'π' })
}
const renderLabel = ({ option }) => {
return h('span', { style: { fontWeight: 'bold' } }, option.label)
}
const renderSuffix = ({ option }) => {
return h(NButton, { size: 'tiny', text: true }, { default: () => 'Edit' })
}
</script>
```
### File Tree
```vue
<template>
<n-tree
block-line
expand-on-click
:data="fileData"
:render-switcher-icon="renderSwitcherIcon"
/>
</template>
<script setup>
import { h } from 'vue'
const fileData = [
{
key: 'src',
label: 'src',
isLeaf: false,
children: [
{ key: 'src/main.js', label: 'main.js', isLeaf: true },
{ key: 'src/App.vue', label: 'App.vue', isLeaf: true }
]
}
]
const renderSwitcherIcon = ({ option, expanded }) => {
if (option.isLeaf) return null
return h('span', expanded ? 'π' : 'π')
}
</script>
```
### Context Menu
```vue
<template>
<n-tree
block-line
:data="data"
:node-props="nodeProps"
/>
<n-dropdown
trigger="manual"
:show="showDropdown"
:options="dropdownOptions"
:x="x"
:y="y"
@select="handleDropdownSelect"
@clickoutside="showDropdown = false"
/>
</template>
<script setup>
import { ref } from 'vue'
const showDropdown = ref(false)
const x = ref(0)
const y = ref(0)
const currentNode = ref(null)
const nodeProps = ({ option }) => ({
onClick: (e) => {
e.preventDefault()
currentNode.value = option
showDropdown.value = true
x.value = e.clientX
y.value = e.clientY
},
onContextmenu: (e) => {
e.preventDefault()
currentNode.value = option
showDropdown.value = true
x.value = e.clientX
y.value = e.clientY
}
})
const dropdownOptions = [
{ label: 'Add Child', key: 'add' },
{ label: 'Edit', key: 'edit' },
{ label: 'Delete', key: 'delete' }
]
const handleDropdownSelect = (key) => {
showDropdown.value = false
// Handle action
}
</script>
```
## Best Practices
### 1. Always Define Unique Keys
```vue
const data = [
{ key: 'unique-id-1', label: 'Node 1' },
{ key: 'unique-id-2', label: 'Node 2' }
]
```
### 2. Use Virtual Scroll for Large Trees
```vue
<n-tree
:data="largeData"
virtual-scroll
style="height: 400px"
/>
```
### 3. Use Controlled Mode for Dynamic Data
```vue
<n-tree
:expanded-keys="expandedKeys"
:checked-keys="checkedKeys"
:selected-keys="selectedKeys"
@update:expanded-keys="handleExpandedChange"
@update:checked-keys="handleCheckedChange"
@update:selected-keys="handleSelectedChange"
/>
```
### 4. Set isLeaf for Async Loading
```vue
const data = [
{ key: '1', label: 'Folder', isLeaf: false }, // Will show expand icon
{ key: '2', label: 'File', isLeaf: true } // No expand icon
]
```
### 5. Use block-line for Better Click Area
```vue
<n-tree block-line :data="data" />
```
### 6. Handle Empty States
```vue
<n-tree :data="data">
<template #empty>
<n-empty description="No data available" />
</template>
</n-tree>
```
Attribution
Comments
Loading commentsβ¦
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.