TreeSelect

Branches open within the dropdown; in single and multiple mode, leaves are selectable.

Basics

Search and choose a leaf from a nested tree.

Loading demo…

import { Block, TreeSelect } from '@plocks/ui'; const data = [ { id: 'fruit', label: 'Fruit', children: [ { id: 'apple', label: 'Apple' }, { id: 'banana', label: 'Banana' }, ], }, { id: 'vegetable', label: 'Vegetable', children: [{ id: 'carrot', label: 'Carrot' }] }, ]; export function Demo() { return ( <Block fullWidth> <TreeSelect label="Food" data={data} searchable /> </Block> ); }

Multiple Selection

Choose several leaves from the same tree.

Loading demo…

import { Block, TreeSelect } from '@plocks/ui'; const data = [ { id: 'fruit', label: 'Fruit', children: [ { id: 'apple', label: 'Apple' }, { id: 'banana', label: 'Banana' }, ], }, { id: 'vegetable', label: 'Vegetable', children: [{ id: 'carrot', label: 'Carrot' }] }, ]; export function Demo() { return ( <Block fullWidth> <TreeSelect label="Food" data={data} mode="multiple" defaultValue={['apple']} /> </Block> ); }

Checkbox Selection

Checkbox mode can check several leaves.

Loading demo…

import { Block, TreeSelect } from '@plocks/ui'; const data = [ { id: 'fruit', label: 'Fruit', children: [ { id: 'apple', label: 'Apple' }, { id: 'banana', label: 'Banana' }, ], }, { id: 'vegetable', label: 'Vegetable', children: [{ id: 'carrot', label: 'Carrot' }] }, ]; export function Demo() { return ( <Block fullWidth> <TreeSelect label="Food" data={data} mode="checkbox" /> </Block> ); }

Clearable

clearable adds a button to clear a selected value.

Loading demo…

import { Block, TreeSelect } from '@plocks/ui'; const data = [ { id: 'fruit', label: 'Fruit', children: [ { id: 'apple', label: 'Apple' }, { id: 'banana', label: 'Banana' }, ], }, { id: 'vegetable', label: 'Vegetable', children: [{ id: 'carrot', label: 'Carrot' }] }, ]; export function Demo() { return ( <Block fullWidth> <TreeSelect label="Food" data={data} defaultValue="apple" clearable /> </Block> ); }

Expanded Branches

defaultExpandAll opens all branches initially.

Loading demo…

import { Block, TreeSelect } from '@plocks/ui'; const data = [ { id: 'fruit', label: 'Fruit', children: [ { id: 'apple', label: 'Apple' }, { id: 'banana', label: 'Banana' }, ], }, { id: 'vegetable', label: 'Vegetable', children: [{ id: 'carrot', label: 'Carrot' }] }, ]; export function Demo() { return ( <Block fullWidth> <TreeSelect label="Food" data={data} defaultExpandAll /> </Block> ); }

Connecting Lines

withLines draws branch guides in the tree.

Loading demo…

import { Block, TreeSelect } from '@plocks/ui'; const data = [ { id: 'fruit', label: 'Fruit', children: [ { id: 'apple', label: 'Apple' }, { id: 'banana', label: 'Banana' }, ], }, { id: 'vegetable', label: 'Vegetable', children: [{ id: 'carrot', label: 'Carrot' }] }, ]; export function Demo() { return ( <Block fullWidth> <TreeSelect label="Food" data={data} withLines /> </Block> ); }

Strict Checking

checkStrictly stops cascading checkbox changes.

Loading demo…

import { Block, TreeSelect } from '@plocks/ui'; const data = [ { id: 'fruit', label: 'Fruit', children: [ { id: 'apple', label: 'Apple' }, { id: 'banana', label: 'Banana' }, ], }, { id: 'vegetable', label: 'Vegetable', children: [{ id: 'carrot', label: 'Carrot' }] }, ]; export function Demo() { return ( <Block fullWidth> <TreeSelect label="Food" data={data} mode="checkbox" checkStrictly /> </Block> ); }

Maximum Values

maxValues caps a multiple selection.

Loading demo…

import { Block, TreeSelect } from '@plocks/ui'; const data = [ { id: 'fruit', label: 'Fruit', children: [ { id: 'apple', label: 'Apple' }, { id: 'banana', label: 'Banana' }, ], }, { id: 'vegetable', label: 'Vegetable', children: [{ id: 'carrot', label: 'Carrot' }] }, ]; export function Demo() { return ( <Block fullWidth> <TreeSelect label="Food" data={data} mode="multiple" maxValues={2} /> </Block> ); }

Custom Node

renderNode changes the content of each tree row.

Loading demo…

import { Block, TreeSelect } from '@plocks/ui'; const data = [ { id: 'fruit', label: 'Fruit', children: [ { id: 'apple', label: 'Apple' }, { id: 'banana', label: 'Banana' }, ], }, { id: 'vegetable', label: 'Vegetable', children: [{ id: 'carrot', label: 'Carrot' }] }, ]; export function Demo() { return ( <Block fullWidth> <TreeSelect label="Food" data={data} renderNode={(node) => `${node.label} •`} /> </Block> ); }

Nothing Found

A fallback message appears when search has no matching nodes.

Loading demo…

import { Block, TreeSelect } from '@plocks/ui'; const data = [ { id: 'fruit', label: 'Fruit', children: [ { id: 'apple', label: 'Apple' }, { id: 'banana', label: 'Banana' }, ], }, { id: 'vegetable', label: 'Vegetable', children: [{ id: 'carrot', label: 'Carrot' }] }, ]; export function Demo() { return ( <Block fullWidth> <TreeSelect label="Food" data={data} searchable nothingFoundMessage="No matching food" /> </Block> ); }
position changes where the tree opens.

Loading demo…

import { Block, TreeSelect } from '@plocks/ui'; const data = [ { id: 'fruit', label: 'Fruit', children: [ { id: 'apple', label: 'Apple' }, { id: 'banana', label: 'Banana' }, ], }, { id: 'vegetable', label: 'Vegetable', children: [{ id: 'carrot', label: 'Carrot' }] }, ]; export function Demo() { return ( <Block fullWidth> <TreeSelect label="Food" data={data} position="top-start" dropdownWidth={320} /> </Block> ); }

Searchable

searchable filters nested nodes by their labels.

Loading demo…

import { Block, TreeSelect } from '@plocks/ui'; const data = [ { id: 'fruit', label: 'Fruit', children: [ { id: 'apple', label: 'Apple' }, { id: 'banana', label: 'Banana' }, ], }, { id: 'vegetable', label: 'Vegetable', children: [ { id: 'carrot', label: 'Carrot' }, { id: 'lettuce', label: 'Lettuce' }, ], }, ]; export function Demo() { return ( <Block fullWidth> <TreeSelect label="Food" data={data} searchable /> </Block> ); }

Controlled Value

value and onChange keep selection in parent state.

Loading demo…

import { useState } from 'react'; import { Block, TreeSelect } from '@plocks/ui'; const data = [ { id: 'fruit', label: 'Fruit', children: [ { id: 'apple', label: 'Apple' }, { id: 'banana', label: 'Banana' }, ], }, ]; export function Demo() { const [value, setValue] = useState<string | null>('apple'); return ( <Block fullWidth> <TreeSelect label="Food" data={data} value={value} onChange={setValue} clearable /> </Block> ); }
offset sets the gap between the field and its dropdown.

Loading demo…

import { Block, TreeSelect } from '@plocks/ui'; const data = [ { id: 'fruit', label: 'Fruit', children: [ { id: 'apple', label: 'Apple' }, { id: 'banana', label: 'Banana' }, ], }, ]; export function Demo() { return ( <Block fullWidth> <TreeSelect label="Food" data={data} offset={16} /> </Block> ); }

Field States

readOnly, disabled, and error use the shared field behavior.

Loading demo…

import { Block, Flex, TreeSelect } from '@plocks/ui'; const data = [{ id: 'fruit', label: 'Fruit', children: [{ id: 'apple', label: 'Apple' }] }]; export function Demo() { return ( <Block fullWidth> <Flex direction="column" gap="md"> <TreeSelect label="Read only" data={data} defaultValue="apple" readOnly /> <TreeSelect label="Disabled" data={data} disabled /> <TreeSelect label="Required" data={data} required error="Choose an item" /> </Flex> </Block> ); }
Scan to open this page on your phoneplocks.dev/ui/TreeSelect