TreeSelect
Dropdown tree selection for hierarchical data.
Import
Section titled “Import”import { TreeSelect } from 'asterui'import type { TreeDataNode } from 'asterui'Examples
Section titled “Examples”Basic TreeSelect
Select a single value from a tree structure.
Select an item
import { TreeSelect } from 'asterui'
import type { TreeDataNode } from 'asterui'
import { useState } from 'react'
function App() {
const basicTreeData: TreeDataNode[] = [
{
key: 'parent',
title: 'Parent Node',
children: [
{
key: 'child1',
title: 'Child Node 1',
children: [
{ key: 'leaf1', title: 'Leaf 1' },
{ key: 'leaf2', title: 'Leaf 2' },
],
},
{ key: 'child2', title: 'Child Node 2' },
],
},
]
const [value, setValue] = useState<string | undefined>()
return (
<TreeSelect
treeData={basicTreeData}
value={value}
onChange={(val) => setValue(val as string)}
placeholder="Select an item"
/>
)
}
export default App Multiple Selection
Select multiple values from the tree.
Select items
import { TreeSelect } from 'asterui'
import type { TreeDataNode } from 'asterui'
import { useState } from 'react'
function App() {
const categoriesData: TreeDataNode[] = [
{
key: 'electronics',
title: 'Electronics',
children: [
{
key: 'phones',
title: 'Phones',
children: [
{ key: 'iphone', title: 'iPhone' },
{ key: 'samsung', title: 'Samsung' },
{ key: 'pixel', title: 'Pixel' },
],
},
{
key: 'laptops',
title: 'Laptops',
children: [
{ key: 'macbook', title: 'MacBook' },
{ key: 'thinkpad', title: 'ThinkPad' },
],
},
],
},
{
key: 'clothing',
title: 'Clothing',
children: [
{ key: 'shirts', title: 'Shirts' },
{ key: 'pants', title: 'Pants' },
{ key: 'shoes', title: 'Shoes' },
],
},
]
const [value, setValue] = useState<string[]>([])
return (
<TreeSelect
treeData={categoriesData}
value={value}
onChange={(val) => setValue(val as string[])}
placeholder="Select items"
multiple
/>
)
}
export default App Checkable
Use checkboxes for selection with parent-child association.
Check items
import { TreeSelect } from 'asterui'
import type { TreeDataNode } from 'asterui'
import { useState } from 'react'
function App() {
const categoriesData: TreeDataNode[] = [
{
key: 'electronics',
title: 'Electronics',
children: [
{
key: 'phones',
title: 'Phones',
children: [
{ key: 'iphone', title: 'iPhone' },
{ key: 'samsung', title: 'Samsung' },
{ key: 'pixel', title: 'Pixel' },
],
},
{
key: 'laptops',
title: 'Laptops',
children: [
{ key: 'macbook', title: 'MacBook' },
{ key: 'thinkpad', title: 'ThinkPad' },
],
},
],
},
{
key: 'clothing',
title: 'Clothing',
children: [
{ key: 'shirts', title: 'Shirts' },
{ key: 'pants', title: 'Pants' },
{ key: 'shoes', title: 'Shoes' },
],
},
]
const [value, setValue] = useState<string[]>([])
return (
<TreeSelect
treeData={categoriesData}
value={value}
onChange={(val) => setValue(val as string[])}
placeholder="Check items"
treeCheckable
/>
)
}
export default App Searchable
Filter tree nodes with search.
Search and select
import { TreeSelect } from 'asterui'
import type { TreeDataNode } from 'asterui'
import { useState } from 'react'
function App() {
const categoriesData: TreeDataNode[] = [
{
key: 'electronics',
title: 'Electronics',
children: [
{
key: 'phones',
title: 'Phones',
children: [
{ key: 'iphone', title: 'iPhone' },
{ key: 'samsung', title: 'Samsung' },
{ key: 'pixel', title: 'Pixel' },
],
},
{
key: 'laptops',
title: 'Laptops',
children: [
{ key: 'macbook', title: 'MacBook' },
{ key: 'thinkpad', title: 'ThinkPad' },
],
},
],
},
{
key: 'clothing',
title: 'Clothing',
children: [
{ key: 'shirts', title: 'Shirts' },
{ key: 'pants', title: 'Pants' },
{ key: 'shoes', title: 'Shoes' },
],
},
]
const [value, setValue] = useState<string | undefined>()
return (
<TreeSelect
treeData={categoriesData}
value={value}
onChange={(val) => setValue(val as string)}
placeholder="Search and select"
showSearch
/>
)
}
export default App Sizes
TreeSelect comes in various sizes.
Extra small
Small
Medium
Large
Extra large
import { TreeSelect } from 'asterui'
import type { TreeDataNode } from 'asterui'
function App() {
const simpleData: TreeDataNode[] = [
{ key: 'opt1', title: 'Option 1' },
{ key: 'opt2', title: 'Option 2' },
{ key: 'opt3', title: 'Option 3' },
]
return (
<div className="flex flex-col gap-2">
<TreeSelect treeData={simpleData} size="xs" placeholder="Extra small" />
<TreeSelect treeData={simpleData} size="sm" placeholder="Small" />
<TreeSelect treeData={simpleData} size="md" placeholder="Medium" />
<TreeSelect treeData={simpleData} size="lg" placeholder="Large" />
<TreeSelect treeData={simpleData} size="xl" placeholder="Extra large" />
</div>
)
}
export default App Validation Status
Show error or warning states.
Error state
Warning state
import { TreeSelect } from 'asterui'
import type { TreeDataNode } from 'asterui'
function App() {
const simpleData: TreeDataNode[] = [
{ key: 'opt1', title: 'Option 1' },
{ key: 'opt2', title: 'Option 2' },
{ key: 'opt3', title: 'Option 3' },
]
return (
<div className="flex flex-col gap-2">
<TreeSelect treeData={simpleData} status="error" placeholder="Error state" />
<TreeSelect treeData={simpleData} status="warning" placeholder="Warning state" />
</div>
)
}
export default App Async Loading
Load tree nodes asynchronously when expanded.
Expand to load
import { TreeSelect } from 'asterui'
import type { TreeDataNode } from 'asterui'
import { useState } from 'react'
function App() {
const [treeData, setTreeData] = useState<TreeDataNode[]>([
{ key: 'region1', title: 'Region 1' },
{ key: 'region2', title: 'Region 2' },
])
const loadData = async (node: TreeDataNode) => {
await new Promise((resolve) => setTimeout(resolve, 1000))
setTreeData((prev) => {
const updateNode = (nodes: TreeDataNode[]): TreeDataNode[] =>
nodes.map((n) =>
n.key === node.key
? {
...n,
children: [
{ key: `${n.key}-1`, title: 'Child 1', isLeaf: true },
{ key: `${n.key}-2`, title: 'Child 2', isLeaf: true },
],
}
: { ...n, children: n.children ? updateNode(n.children) : undefined }
)
return updateNode(prev)
})
}
return (
<TreeSelect
treeData={treeData}
loadData={loadData}
placeholder="Expand to load"
/>
)
}
export default App Max Tag Count
Limit the number of visible tags.
Select items
import { TreeSelect } from 'asterui'
import type { TreeDataNode } from 'asterui'
import { useState } from 'react'
function App() {
const categoriesData: TreeDataNode[] = [
{
key: 'electronics',
title: 'Electronics',
children: [
{
key: 'phones',
title: 'Phones',
children: [
{ key: 'iphone', title: 'iPhone' },
{ key: 'samsung', title: 'Samsung' },
{ key: 'pixel', title: 'Pixel' },
],
},
{
key: 'laptops',
title: 'Laptops',
children: [
{ key: 'macbook', title: 'MacBook' },
{ key: 'thinkpad', title: 'ThinkPad' },
],
},
],
},
{
key: 'clothing',
title: 'Clothing',
children: [
{ key: 'shirts', title: 'Shirts' },
{ key: 'pants', title: 'Pants' },
{ key: 'shoes', title: 'Shoes' },
],
},
]
const [value, setValue] = useState<string[]>([])
return (
<TreeSelect
treeData={categoriesData}
value={value}
onChange={(val) => setValue(val as string[])}
placeholder="Select items"
treeCheckable
maxTagCount={2}
maxTagPlaceholder={(omitted) => `+${omitted.length} more...`}
/>
)
}
export default App Tree Line
Show connecting lines between tree nodes.
With tree lines
import { TreeSelect } from 'asterui'
import type { TreeDataNode } from 'asterui'
function App() {
const basicTreeData: TreeDataNode[] = [
{
key: 'parent',
title: 'Parent Node',
children: [
{
key: 'child1',
title: 'Child Node 1',
children: [
{ key: 'leaf1', title: 'Leaf 1' },
{ key: 'leaf2', title: 'Leaf 2' },
],
},
{ key: 'child2', title: 'Child Node 2' },
],
},
]
return (
<TreeSelect
treeData={basicTreeData}
placeholder="With tree lines"
treeLine
treeDefaultExpandAll
/>
)
}
export default App Disabled Items
Some tree nodes can be disabled.
Select an item
import { TreeSelect } from 'asterui'
import type { TreeDataNode } from 'asterui'
import { useState } from 'react'
function App() {
const [value, setValue] = useState<string | undefined>()
const treeDataWithDisabled: TreeDataNode[] = [
{
key: 'parent',
title: 'Available Parent',
children: [
{ key: 'child1', title: 'Available Child' },
{ key: 'child2', title: 'Disabled Child', disabled: true },
{ key: 'child3', title: 'Another Available' },
],
},
]
return (
<TreeSelect
treeData={treeDataWithDisabled}
value={value}
onChange={(val) => setValue(val as string)}
placeholder="Select an item"
/>
)
}
export default App TreeSelect
Section titled “TreeSelect”| Property | Description | Type | Default |
|---|---|---|---|
treeData | Tree data structure | TreeDataNode[] | [] |
value | Selected value(s) | string | string[] | - |
defaultValue | Default selected value(s) | string | string[] | [] |
onChange | Callback when selection changes | (value: string | string[], labels: ReactNode[]) => void | - |
multiple | Allow multiple selection | boolean | false |
treeCheckable | Show checkbox for tree nodes | boolean | false |
treeCheckStrictly | Check without parent-child association | boolean | false |
showCheckedStrategy | How to display checked items | 'SHOW_ALL' | 'SHOW_PARENT' | 'SHOW_CHILD' | 'SHOW_ALL' |
showSearch | Enable search functionality | boolean | false |
searchValue | Controlled search input value | string | - |
onSearch | Callback when search input changes | (value: string) => void | - |
filterTreeNode | Custom filter function | (searchValue: string, node: TreeDataNode) => boolean | - |
placeholder | Placeholder text | string | 'Please select' |
allowClear | Show clear button | boolean | true |
disabled | Disable the select | boolean | false |
size | Size of the input | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' |
color | Color theme | 'primary' | 'secondary' | 'accent' | 'info' | 'success' | 'warning' | 'error' | - |
status | Validation status | 'error' | 'warning' | - |
maxTagCount | Max number of tags to show | number | 'responsive' | - |
maxTagPlaceholder | Content for hidden tags | ReactNode | ((omittedValues: string[]) => ReactNode) | - |
treeLine | Show connecting lines | boolean | false |
treeDefaultExpandAll | Expand all tree nodes by default | boolean | false |
treeDefaultExpandedKeys | Default expanded tree node keys | string[] | [] |
treeExpandedKeys | Controlled expanded tree node keys | string[] | - |
onTreeExpand | Callback when tree nodes expand/collapse | (expandedKeys: string[]) => void | - |
loadData | Load data asynchronously | (node: TreeDataNode) => Promise<void> | - |
fieldNames | Customize field names | { label?: string; value?: string; children?: string } | - |
open | Controlled dropdown visibility | boolean | - |
onDropdownVisibleChange | Callback when dropdown visibility changes | (open: boolean) => void | - |
suffixIcon | Custom suffix icon | ReactNode | - |
switcherIcon | Custom expand/collapse icon | ReactNode | ((props: { expanded: boolean }) => ReactNode) | - |
notFoundContent | Content when no results | ReactNode | 'No results found' |
dropdownRender | Custom dropdown content renderer | (menu: ReactNode) => ReactNode | - |
popupClassName | Class for dropdown | string | - |
data-testid | Test ID for the component | string | 'treeselect' |
className | Additional CSS classes | string | - |
TreeDataNode
Section titled “TreeDataNode”| Property | Description | Type | Default |
|---|---|---|---|
key | Unique identifier | string | - |
title | Display text | ReactNode | - |
children | Child nodes | TreeDataNode[] | - |
disabled | Whether disabled | boolean | - |
isLeaf | Whether this is a leaf node | boolean | - |
Accessibility
Section titled “Accessibility”The TreeSelect component follows WAI-ARIA combobox and tree patterns with proper ARIA attributes and keyboard navigation.
Keyboard Navigation
Section titled “Keyboard Navigation”| Key | Action |
|---|---|
| Enter / Space | Open dropdown or select focused item |
| Escape | Close dropdown |
| ↓ | Open dropdown or move focus down |
| ↑ | Move focus up |
| → | Expand focused tree node |
| ← | Collapse focused tree node or move to parent |
| Home | Move focus to first item |
| End | Move focus to last item |
Testing
Section titled “Testing”The component exposes data-testid and data-state attributes for testing:
| Element | Test ID | Data Attributes |
|---|---|---|
| Root | {baseTestId} | data-state="open|closed", data-disabled |
| Trigger | {baseTestId}-trigger | - |
| Dropdown | {baseTestId}-dropdown | - |
| Search | {baseTestId}-search | - |
| Clear | {baseTestId}-clear | - |
| Option | {baseTestId}-option-{key} | data-state="selected|unselected", data-disabled |
| Tag | {baseTestId}-tag-{key} | - |
| Empty | {baseTestId}-empty | - |
Pass a custom data-testid prop to use a different base ID.