esc
navigate select
Browse documentation

Expansion

Which branches are open is just another piece of state — controlled or not, your choice.

Engineering
Show code
<TreeKit
  data={data}
  expandedIds={expandedIds}
  onExpandedChange={setExpandedIds}
/>
PropTypeDefaultDescription
defaultExpandedIdsstring[]Initial expanded ids for uncontrolled usage.
expandedIdsstring[]Controlled expanded ids — pair with onExpandedChange.
onExpandedChange(ids: string[]) => voidFires whenever the expanded set changes, for either usage.
onNodeExpand(node: TreeNode) => voidFires once, specifically when a node transitions to expanded.
onNodeCollapse(node: TreeNode) => voidFires once, specifically when a node transitions to collapsed.
expandTrigger"chevron" | "label" | "either""either"What triggers expand/collapse for a node with children.

The whole row is clickable by default

expandTrigger defaults to "either": clicking anywhere on a parent row's body — not just the small chevron — expands or collapses it. A row that visually looks clickable (hover state, pointer cursor) but only reacts to a 6px chevron is a common source of "I clicked it and nothing happened" complaints, so TreeKit avoids it by default.

Set expandTrigger="chevron" to go back to precise-only clicks — useful when a row click needs to mean something else instead, e.g. opening a detail panel via onNodeClick without also toggling expansion. The chevron itself always works regardless of this setting.

The equivalent for leaf rows (nodes with no children) is checkOnClick, which defaults to true: clicking anywhere on a leaf row toggles its checkbox, not just the checkbox's own hit target. See Selection.

Programmatic expand/collapse all

There's no dedicated expandAll prop — instead, compute the full id list once with the exported collectExpandableIds utility (or expandAllIds, which does the same from a prebuilt index) and set it as your controlled expandedIds:

import { buildTreeIndex, collectExpandableIds } from "@treekit-ui/core";

const index = buildTreeIndex(data);
const allExpandableIds = collectExpandableIds(index);

<button onClick={() => setExpandedIds(allExpandableIds)}>Expand all</button>
<button onClick={() => setExpandedIds([])}>Collapse all</button>

The chevron is always keyboard-reachable

Regardless of expandTrigger, expands and collapses (or moves focus to the parent) the focused node — see Accessibility. The chevron itself is a decorative pointer affordance rather than a second focusable control nested inside the row, which avoids the double-tab-stop problem some tree implementations have.