Patterns
Tree selection
A multi-select file/folder tree built by composing the Tree view with our base Checkbox - giving hierarchical lists the same checkbox styling, focus ring, and tri-state (indeterminate) treatment used everywhere else in the system.
Why this pattern
React Aria's Tree handles keyboard navigation, expand/collapse, and selection state, but it doesn't render a selection checkbox by itself and has no concept of cascading (tri-state) selection - selecting a parent doesn't select its children. TreeView layers both on top: its item content renders CheckboxBase from components/base/checkbox/checkbox.tsx for the selection control, and a small utility (cascadeSelection in tree-view-utils.ts) diffs each selection change to propagate it up and down the hierarchy and to mark partially-selected parents as indeterminate.
The result: reuse the same checkbox visual language (colours, focus-visible ring, disabled state) inside a component that would otherwise need its own bespoke selection indicator.
Example
Selecting Design selects all of its descendants; selecting only some children of Engineering leaves it indeterminate.
Multi-select, with connectors
Usage
import { TreeView } from "@/components/application/tree-view/tree-view";
import { Folder, File02 } from "@untitledui/icons";
<TreeView aria-label="Project files" selectionMode="multiple" showConnectors>
<TreeView.Item id="design" textValue="Design">
<TreeView.ItemContent icon={Folder}>Design</TreeView.ItemContent>
<TreeView.Item id="design-components" textValue="Components">
<TreeView.ItemContent icon={File02}>Components</TreeView.ItemContent>
</TreeView.Item>
</TreeView.Item>
</TreeView>The checkbox only appears when selectionMode is "single" or "multiple"; leave it "none" (the default) for a plain navigable tree.
A plain navigable tree with no connector lines can use alignLeaves on the tree, which leaves the chevron's place empty on an item with no children so its label lines up with its siblings' and a child sits one step in from its parent, and weight="normal" on an item's content, which lets leaves recede beside semibold parents.
Navigation, no checkboxes, aligned leaves
Notes
- Selection checkboxes are always rendered at the
smcheckbox size, regardless of the tree's ownsizeprop. alignLeavesandweightare opt-in: left out, the tree draws as before.alignLeavesdoes nothing withshowConnectors, which already reserves the place.- Indeterminate parents render with the same dash glyph as a standalone indeterminate
Checkbox. - This is a composition pattern, not a standalone component - see
components/application/tree-view/tree-view.tsxfor the implementation.