Navigation
TreeSelect
Dropdown select whose options are a hierarchical tree, with expandable branches, full keyboard navigation, and optional multi-select chips, cascading checkboxes, search and a clear button.
Import
import { TreeSelect } from "@zephora/react";Examples
Basic
Branches expand with the chevron; leaves select and close the popup.
<TreeSelect
placeholder="Pick a location"
defaultExpandedKeys={["eu"]}
data={[
{
key: "eu",
label: "Europe",
children: [
{ key: "de", label: "Germany" },
{ key: "fr", label: "France" },
],
},
{
key: "na",
label: "North America",
children: [
{ key: "us", label: "United States" },
{ key: "ca", label: "Canada", disabled: true },
],
},
]}
/>Controlled
`value` + `onValueChange` control the selected key (null clears it).
Selected key: docs
const [value, setValue] = React.useState<string | null>("docs");
<TreeSelect
value={value}
onValueChange={setValue}
defaultExpandedKeys={["workspace"]}
data={[
{
key: "workspace",
label: "Workspace",
children: [
{ key: "docs", label: "Docs" },
{ key: "assets", label: "Assets" },
],
},
]}
/>
<p>Selected key: {value ?? "none"}</p>Multiple with checkboxes and search
`checkable` renders cascading checkboxes (checking a branch checks its subtree; partial branches report aria-checked="mixed") and implies multiple values. `searchable` filters nodes, `allowClear` clears everything, and `expandedKeys` can be controlled.
Germany
Values: de
const [values, setValues] = React.useState<string[]>(["de"]);
const [expanded, setExpanded] = React.useState<string[]>(["eu"]);
<TreeSelect
placeholder="Pick regions"
checkable
searchable
allowClear
values={values}
onValuesChange={setValues}
expandedKeys={expanded}
onExpandedChange={setExpanded}
data={[
{
key: "eu",
label: "Europe",
children: [
{ key: "de", label: "Germany" },
{ key: "fr", label: "France" },
],
},
{
key: "na",
label: "North America",
children: [
{ key: "us", label: "United States" },
{ key: "ca", label: "Canada" },
],
},
]}
/>
<p>Values: {values.join(", ") || "none"}</p>API
TreeSelect props
| Prop | Type | Default | Description |
|---|---|---|---|
data * | TreeNode[] — { key: string; label: string; disabled?: boolean; children?: TreeNode[] } | — | Tree of selectable nodes. |
value | string | null | — | Controlled selected key. |
defaultValue | string | null | null | Initial selected key when uncontrolled. |
onValueChange | (value: string | null) => void | — | Called with the selected key (or null when deselected). |
multiple | boolean | false | Allow selecting multiple keys; the trigger renders removable chips. |
checkable | boolean | false | Renders checkboxes on every node. Checking a branch cascades to its descendants; partially-checked branches report aria-checked="mixed". Implies multiple values. |
values / defaultValues / onValuesChange | string[] / string[] / (values: string[]) => void | — | Controlled / uncontrolled selected keys in multiple/checkable mode. |
searchable | boolean | false | Renders a search input at the top of the popup that filters nodes. |
allowClear | boolean | false | Shows a clear button in the trigger when something is selected. |
maxDisplay | number | — | Collapses chips beyond this count into "+N" (multiple/checkable mode). |
placeholder | string | "Select…" | Text shown when nothing is selected. |
size | "sm" | "md" | "lg" | "md" | Trigger size. |
invalid | boolean | false | Marks the field invalid. |
disabled | boolean | false | Disables the trigger. |
expandedKeys | string[] | — | Controlled expanded branch keys. |
defaultExpandedKeys | string[] | — | Keys of branches expanded on first render. |
onExpandedChange | (keys: string[]) => void | — | Called with the new expanded keys array whenever expansion changes. |
unstyled | boolean | false | Headless mode — skips Zephora styling. |
Keyboard
| Key | Action |
|---|---|
ArrowDown | On the trigger: opens the popup. In the tree: next node. |
ArrowUp | Previous visible node. |
ArrowRight | Expands a collapsed branch, or moves to its first child. |
ArrowLeft | Collapses an expanded branch, or moves to the parent. |
Home / End | Jump to the first / last visible node. |
Enter / Space | Selects a leaf (and closes) or toggles a branch's selection. |
Escape | Closes the popup and refocuses the trigger. |