n-tree-select Component
The n-tree-select component combines the functionality of a select dropdown with a tree structure, allowing users to select values from hierarchical data. It's ideal for scenarios like organization charts, file system navigation, category selection, and any nested data structure.
When to Use
Use n-tree-select when you need to:
- Hierarchical Selection: Select values from nested/hierarchical data structures
- Category Selection: Choose categories from a multi-level category tree
- Organization Picker: Select departments or teams from an organizational hierarchy
- File/Folder Selection: Navigate and select files from a folder structure
- Multi-Level Filtering: Filter and select from nested option groups
Basic Usage
Basic Tree Select
<template>
<n-tree-select
:options="options"
default-value="Drive My Car"
@update:value="handleUpdateValue"
/>
</template>
<script setup>
import { ref } from 'vue'
const options = [
{
label: 'Rubber Soul',
key: 'Rubber Soul',
children: [
{ label: 'Drive My Car', key: 'Drive My Car' },
{ label: 'Norwegian Wood', key: 'Norwegian Wood' }
]
},
{
label: 'Let It Be',
key: 'Let It Be',
children: [
{ label: 'Dig It', key: 'Dig It' },
{ label: 'Let It Be', key: 'Let It Be' }
]
}
]
const handleUpdateValue = (value, option) => {
console.log(value, option)
}
</script>
Multiple Selection
<template>
<n-tree-select
multiple
:options="options"
:default-value="['Norwegian Wood']"
@update:value="handleUpdateValue"
/>
</template>
Checkable with Cascade
<template>
<n-tree-select
multiple
cascade
checkable
:options="options"
:default-value="['Norwegian Wood']"
/>
</template>
Filterable
<template>
<n-space vertical>
<n-tree-select
filterable
:options="options"
default-value="Drive My Car"
clearable
/>
<n-tree-select
multiple
checkable
filterable
:clear-filter-after-select="false"
:options="options"
:default-value="['Norwegian Wood']"
clearable
/>
</n-space>
</template>
Show Line
<template>
<n-tree-select
show-line
default-expand-all
:options="options"
default-value="Drive My Car"
/>
</template>
API Reference
Props
| Name | Type | Default | Description |
|---|---|---|---|
value / v-model:value |
string | number | Array<string | number> | null |
undefined |
Selected key(s). Use array for multiple selection. |
default-value |
string | number | Array<string | number> | null |
null |
Default uncontrolled value. |
options |
TreeSelectOption[] |
[] |
Tree options data. |
multiple |
boolean |
false |
Allow multiple selections. |
checkable |
boolean |
false |
Show checkboxes for selection. |
cascade |
boolean |
false |
Link parent and child selection. |
check-strategy |
'all' | 'parent' | 'child' |
'all' |
How to display selected nodes when both parent and children are selected. |
filterable |
boolean |
false |
Enable search filtering. |
filter |
(pattern: string, option: TreeSelectOption) => boolean |
- | Custom filter function. |
clearable |
boolean |
false |
Allow clearing selection. |
clear-filter-after-select |
boolean |
true |
Clear filter keyword after selection in multiple mode. |
disabled |
boolean |
false |
Disable the component. |
placeholder |
string |
'Please Select' |
Placeholder text. |
size |
'small' | 'medium' | 'large' |
'medium' |
Component size. |
loading |
boolean |
false |
Show loading state. |
default-expand-all |
boolean |
false |
Expand all nodes by default. |
default-expanded-keys |
Array<string | number> |
[] |
Default expanded keys. |
expanded-keys |
Array<string | number> |
undefined |
Controlled expanded keys. |
show-line |
boolean |
false |
Show tree connecting lines. |
show-path |
boolean |
false |
Show hierarchy path in selected label. |
separator |
string |
' / ' |
Separator for path display. |
indent |
number |
24 |
Indentation per level. |
max-tag-count |
number | 'responsive' |
undefined |
Max tags to display in multiple mode. |
virtual-scroll |
boolean |
true |
Enable virtual scrolling. |
consistent-menu-width |
boolean |
true |
Match menu width to input width. |
placement |
'top-start' | 'top' | 'top-end' | 'right-start' | 'right' | 'right-end' | 'bottom-start' | 'bottom' | 'bottom-end' | 'left-start' | 'left' | 'left-end' |
'bottom-start' |
Menu placement. |
status |
'success' | 'warning' | 'error' |
undefined |
Validation status. |
to |
string | HTMLElement | false |
body |
Container for the menu. |
key-field |
string |
'key' |
Field name for option keys. |
label-field |
string |
'label' |
Field name for option labels. |
children-field |
string |
'children' |
Field name for child options. |
disabled-field |
string |
'disabled' |
Field name for disabled state. |
allow-checking-not-loaded |
boolean |
false |
Allow cascade checking on unloaded nodes. |
indeterminate-keys |
Array<string | number> |
undefined |
Indeterminate keys. |
menu-props |
HTMLAttributes |
undefined |
Menu DOM attributes. |
node-props |
(info: { option: TreeSelectOption }) => HTMLAttributes |
undefined |
Node DOM attributes. |
render-label |
(info: { option: TreeSelectOption, checked: boolean, selected: boolean }) => VNodeChild |
undefined |
Custom label renderer. |
render-prefix |
(info: { option: TreeSelectOption, checked: boolean, selected: boolean }) => VNodeChild |
undefined |
Custom prefix renderer. |
render-suffix |
(info: { option: TreeSelectOption, checked: boolean, selected: boolean }) => VNodeChild |
undefined |
Custom suffix renderer. |
render-switcher-icon |
() => VNodeChild |
undefined |
Custom switcher icon renderer. |
render-tag |
(props: { option: TreeSelectOption, handleClose: () => void }) => VNodeChild |
undefined |
Custom tag renderer. |
override-default-node-click-behavior |
(info: { option: TreeSelectOption }) => 'toggleExpand' | 'toggleSelect' | 'toggleCheck' | 'default' | 'none' |
undefined |
Override node click behavior. |
get-children |
(option: any) => unknown |
undefined |
Get children function. |
ellipsis-tag-popover-props |
PopoverProps |
undefined |
Props for ellipsis tag popover. |
TreeSelectOption Properties
| Name | Type | Description |
|---|---|---|
key |
string | number |
Unique key for the option. |
label |
string |
Display label. |
children |
TreeSelectOption[] |
Child options. |
disabled |
boolean |
Whether the option is disabled. |
isLeaf |
boolean |
Whether the node is a leaf (required for async loading). |
Events
| Name | Parameters | Description |
|---|---|---|
update:value |
(value: string | number | Array<string | number> | null, option: TreeSelectOption | null | Array<TreeSelectOption | null>, meta: { node: TreeOption | null, action: 'select' | 'unselect' | 'delete' | 'clear' }) => void |
Emitted when value changes. |
update:expanded-keys |
(value: Array<string | number>, meta: { node: TreeOption | null, action: 'expand' | 'collapse' | 'filter' }) => void |
Emitted when expanded keys change. |
update:indeterminate-keys |
(keys: Array<string | number>) => void |
Emitted when indeterminate keys change. |
focus |
(e: FocusEvent) => void |
Emitted on focus. |
blur |
(e: FocusEvent) => void |
Emitted on blur. |
load |
(node: TreeSelectOption) => Promise<void> |
Async data loading callback. |
Slots
| Name | Parameters | Description |
|---|---|---|
header |
() |
Header content in the menu. |
action |
() |
Action content at the bottom of the menu. |
arrow |
() |
Custom arrow icon. |
empty |
() |
Empty state content. |
Methods
| Name | Type | Description |
|---|---|---|
focus |
() => void |
Focus the component. |
blur |
() => void |
Blur the component. |
focusInput |
() => void |
Focus the input element. |
blurInput |
() => void |
Blur the input element. |
getCheckedData |
() => { keys: Array<string | number>, options: Array<TreeOption | null> } |
Get checked data. |
getIndeterminateData |
() => { keys: Array<string | number>, options: Array<TreeOption | null> } |
Get indeterminate data. |
Common Patterns
Async Loading
<template>
<n-tree-select
v-model:value="value"
multiple
checkable
:options="options"
:cascade="cascade"
:check-strategy="checkStrategy"
:show-path="showPath"
:allow-checking-not-loaded="cascade"
:on-load="handleLoad"
/>
</template>
<script setup>
import { ref } from 'vue'
const value = ref([])
const options = ref([
{
label: 'Root',
key: 'root',
children: [
{ label: 'Loading...', key: 'child1', isLeaf: false }
]
}
])
const handleLoad = async (node) => {
const children = await fetchChildren(node.key)
node.children = children
}
</script>
Custom Field Names
<template>
<n-tree-select
:options="options"
default-value="Drive My Car"
label-field="whateverLabel"
key-field="whateverKey"
children-field="whateverChildren"
/>
</template>
<script setup>
const options = [
{
whateverLabel: 'Album',
whateverKey: 'album',
whateverChildren: [
{ whateverLabel: 'Song 1', whateverKey: 'song1' }
]
}
]
</script>
Check Strategy Demo
<template>
<n-space vertical>
<n-radio-group v-model:value="checkStrategy">
<n-radio-button value="all">All</n-radio-button>
<n-radio-button value="parent">Parent</n-radio-button>
<n-radio-button value="child">Child</n-radio-button>
</n-radio-group>
<n-tree-select
multiple
cascade
checkable
:check-strategy="checkStrategy"
:options="options"
:default-value="['Dig It', 'go']"
/>
</n-space>
</template>
File Picker Simulation
<template>
<n-tree-select
block-line
:options="fileOptions"
:override-default-node-click-behavior="override"
/>
</template>
<script setup>
const override = ({ option }) => {
if (option.isFolder) {
return 'toggleExpand'
}
return 'toggleSelect'
}
const fileOptions = [
{
label: 'Documents',
key: 'docs',
isFolder: true,
children: [
{ label: 'report.pdf', key: 'report', isFolder: false }
]
}
]
</script>
Validation Status
<template>
<n-space vertical>
<n-tree-select status="warning" placeholder="Warning state" />
<n-tree-select status="error" placeholder="Error state" />
</n-space>
</template>
Custom Rendering
<template>
<n-tree-select
:options="options"
:render-label="renderLabel"
:render-prefix="renderPrefix"
/>
</template>
<script setup>
import { h } from 'vue'
const renderLabel = ({ option, checked, selected }) => {
return h('span', { style: { fontWeight: selected ? 'bold' : 'normal' } }, option.label)
}
const renderPrefix = ({ option }) => {
return h('span', option.children ? '📁' : '📄')
}
const options = [
{
label: 'Folder',
key: 'folder',
children: [
{ label: 'File', key: 'file' }
]
}
]
</script>
Best Practices
Performance
Virtual Scroll: Keep
virtual-scrollenabled (default) for large tree structures.Lazy Loading: Use async loading with
on-loadfor deeply nested trees to improve initial load time.Limit Expanded Nodes: Avoid
default-expand-allfor very large trees; usedefault-expanded-keysinstead.
User Experience
Check Strategy: Choose appropriate
check-strategy:'all': Show all selected nodes (default)'parent': Show only parent when all children selected'child': Show only leaf nodes
Show Path: Enable
show-pathfor deep hierarchies to help users understand the selection context.Filterable: Enable
filterablefor trees with many nodes to help users find options quickly.Clearable: Enable
clearableto allow users to easily reset their selection.
Data Handling
Unique Keys: Ensure each option has a unique
keyproperty.isLeaf Property: Set
isLeaf: falsefor nodes that will have children loaded asynchronously.Custom Field Names: Use
key-field,label-field, andchildren-fieldwhen your data doesn't match the default structure.Cascade Selection: Be careful with
cascadeandallow-checking-not-loadedwhen using async loading, as values may be incomplete.
Form Integration
Validation Status: Use the
statusprop to show validation feedback.Disabled State: Use
disabledto prevent selection in read-only scenarios.Loading State: Show
loadingstate during async operations.