Skip to content

TreeSelect

Dropdown tree selection for hierarchical data.

import { TreeSelect } from 'asterui'
import type { TreeDataNode } from 'asterui'

Basic TreeSelect

Select a single value from a tree structure.

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.

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.

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.

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.

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.

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.

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.

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.

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.

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
PropertyDescriptionTypeDefault
treeDataTree data structureTreeDataNode[][]
valueSelected value(s)string | string[]-
defaultValueDefault selected value(s)string | string[][]
onChangeCallback when selection changes(value: string | string[], labels: ReactNode[]) => void-
multipleAllow multiple selectionbooleanfalse
treeCheckableShow checkbox for tree nodesbooleanfalse
treeCheckStrictlyCheck without parent-child associationbooleanfalse
showCheckedStrategyHow to display checked items'SHOW_ALL' | 'SHOW_PARENT' | 'SHOW_CHILD''SHOW_ALL'
showSearchEnable search functionalitybooleanfalse
searchValueControlled search input valuestring-
onSearchCallback when search input changes(value: string) => void-
filterTreeNodeCustom filter function(searchValue: string, node: TreeDataNode) => boolean-
placeholderPlaceholder textstring'Please select'
allowClearShow clear buttonbooleantrue
disabledDisable the selectbooleanfalse
sizeSize of the input'xs' | 'sm' | 'md' | 'lg' | 'xl''md'
colorColor theme'primary' | 'secondary' | 'accent' | 'info' | 'success' | 'warning' | 'error'-
statusValidation status'error' | 'warning'-
maxTagCountMax number of tags to shownumber | 'responsive'-
maxTagPlaceholderContent for hidden tagsReactNode | ((omittedValues: string[]) => ReactNode)-
treeLineShow connecting linesbooleanfalse
treeDefaultExpandAllExpand all tree nodes by defaultbooleanfalse
treeDefaultExpandedKeysDefault expanded tree node keysstring[][]
treeExpandedKeysControlled expanded tree node keysstring[]-
onTreeExpandCallback when tree nodes expand/collapse(expandedKeys: string[]) => void-
loadDataLoad data asynchronously(node: TreeDataNode) => Promise<void>-
fieldNamesCustomize field names{ label?: string; value?: string; children?: string }-
openControlled dropdown visibilityboolean-
onDropdownVisibleChangeCallback when dropdown visibility changes(open: boolean) => void-
suffixIconCustom suffix iconReactNode-
switcherIconCustom expand/collapse iconReactNode | ((props: { expanded: boolean }) => ReactNode)-
notFoundContentContent when no resultsReactNode'No results found'
dropdownRenderCustom dropdown content renderer(menu: ReactNode) => ReactNode-
popupClassNameClass for dropdownstring-
data-testidTest ID for the componentstring'treeselect'
classNameAdditional CSS classesstring-
PropertyDescriptionTypeDefault
keyUnique identifierstring-
titleDisplay textReactNode-
childrenChild nodesTreeDataNode[]-
disabledWhether disabledboolean-
isLeafWhether this is a leaf nodeboolean-

The TreeSelect component follows WAI-ARIA combobox and tree patterns with proper ARIA attributes and keyboard navigation.

KeyAction
Enter / SpaceOpen dropdown or select focused item
EscapeClose dropdown
↓Open dropdown or move focus down
↑Move focus up
→Expand focused tree node
←Collapse focused tree node or move to parent
HomeMove focus to first item
EndMove focus to last item

The component exposes data-testid and data-state attributes for testing:

ElementTest IDData 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.