Marble UI
← Components

Tree Select

Preview: Default

Select whose options are a tree. Single selection with click, Enter or Space; arrow keys only move focus and expand folders.

Import

import { TreeSelect } from '@marbleui/ui'

Props

<TreeSelect>

PropTypeDefaultDescription
options *requiredTreeOption[]—
classNamestring—
defaultValuestring—
labelstring—
onChange(id: string) => void—
placeholderstring'Select…'

Accessibility

Keyboard interaction, focus management and ARIA semantics come from the underlying primitives: React Aria Button · React Aria Dialog · React Aria DialogTrigger. Follow the primitive's docs for the full keyboard map.

Usage notes

Select whose options form a tree, such as teams inside departments; any node, parent or leaf, can be chosen. To force a leaf choice use Cascader; for a flat list use Select.

const teams: TreeOption[] = [
  { id: 'eng', label: 'Engineering', children: [{ id: 'web', label: 'Web' }, { id: 'mobile', label: 'Mobile' }] },
]
<TreeSelect label="Team" options={teams} defaultValue="web" onChange={(id) => setTeam(id)} />
  • Uncontrolled: defaultValue and onChange use a node id, unique across the tree. The trigger shows the node’s label, not its path.
  • Top-level nodes start expanded, deeper ones collapsed, even if they hold the selection.
  • Opening moves focus into the tree. Arrow keys move focus and expand or collapse folders without choosing; click, Enter or Space chooses a node and closes the popover.
  • The trigger is named by the label plus the chosen node or placeholder.
Example source · show
import { TreeSelect, type TreeOption } from './tree-select'

const teams: TreeOption[] = [
  { id: 'eng', label: 'Engineering', children: [{ id: 'web', label: 'Web' }, { id: 'mobile', label: 'Mobile' }, { id: 'infra', label: 'Infrastructure', children: [{ id: 'sre', label: 'SRE' }, { id: 'data', label: 'Data platform' }] }] },
  { id: 'design', label: 'Design', children: [{ id: 'product', label: 'Product design' }, { id: 'brand', label: 'Brand' }] },
  { id: 'gtm', label: 'Go-to-market', children: [{ id: 'sales', label: 'Sales' }, { id: 'marketing', label: 'Marketing' }] },
]
export const styles = { Default: () => <TreeSelect className="w-72" label="Team" options={teams} />, Preselected: () => <TreeSelect className="w-72" label="Team" options={teams} defaultValue="sre" /> }

Source: packages/ui/src/tree-select/tree-select.tsx

Catalog

Category
selection
Priority
Extra
Primitive
composed
React Aria
Tree + Popover