Pick from hierarchical data — product categories, departments, locations and warehouses, folder destinations, a chart of accounts. A searchable tree in a popover (or inline), single or multi with tri-state cascade, lazy branches, search that keeps ancestors, leaf-only selection and “Add category”. Built on the same useTree model as Tree view.
Products appear under this category in the store menu.
Installation
The whole kit as one package — get updates by bumping the version. Add the styles to globals.css: @import "@vamos-ui/react/styles.css";
Usage
import { Field, TreeSelect, type TreeNode } from "@vamos-ui/react";
<Field label="Category">
<TreeSelect nodes={categories} value={categoryId} onValueChange={setCategoryId} />
</Field>Examples
Multiple with cascade
Checking a parent checks its descendants; a partly checked parent shows a dash (aria-checked="mixed"). valueMode picks what the value lists: all, the topmost checked (parent) or leaves.
Check a country to cover all of its cities. The value lists the topmost checked nodes.
value: ["tr"]
Lazy branches
Nodes with hasChildren load through loadChildren on first open (spinner in the row, retry on error).
Sub-teams load when a branch opens.
Search & leaf-only
Search folds case and accents (also keywords and description), keeps ancestors and opens them. selectable="leaf" lets parents only expand. Enter in the search picks the first match.
Inline with “Add”
mode="inline" for an editor side panel. onCreate adds under the focused node; add the returned node to nodes yourself.
API
TreeSelect
| Prop | Type | Default |
|---|---|---|
nodesThe hierarchy: { id, label, children?, hasChildren?, disabled?, icon?, description?, keywords? }. | TreeNode[] | — |
multipleCheckboxes; value becomes string[]. | boolean | false |
value / defaultValueControlled / initial id(s). | string | null · string[] | — |
onValueChangeCalled with the new value. | (id, node) => void · (ids) => void | — |
cascadeMulti: parent checks descendants (tri-state). | boolean | true |
valueModeMulti + cascade: what a fully checked branch yields. | "all" | "parent" | "leaf" | "all" |
selectableCan parents be chosen? | "any" | "leaf" | "any" |
loadChildrenLazy children for nodes with hasChildren. | (node, signal) => Promise<TreeNode[]> | — |
showPathTrigger shows “Electronics › Phones” (› mirrors in RTL). | boolean | true |
defaultExpandedInitially open branches. | string[] | "selected" | "selected" |
onCreateAdds an “Add” footer that creates under the focused node. | (parentId, label) => TreeNode | Promise<TreeNode> | — |
searchableShow the search box. | boolean | true |
modeinline renders the tree in place. | "popover" | "inline" | "popover" |
heightMax tree height in px. | number | 288 |
sizeTrigger and row size. | "sm" | "md" | "lg" | "md" |
placeholder / searchPlaceholderTrigger and search texts. | string | — |
nameHidden input(s) for form posts. | string | — |
disabled / readOnly / invalid / requiredAlso read from the surrounding Field. | boolean | — |
labelsBuilt-in strings (namespace treeSelect). | Partial<TreeSelectLabels> | — |