Tree View
Hierarchical list with connector guides, expand and collapse, cascading checkbox selection and drag and drop reordering.
Description
TreeView renders nested data as a keyboard-accessible tree. It is built on the React
Aria Tree collection, so rows report treeitem semantics with the right level and
expanded state, arrow keys walk and open branches, and typeahead jumps to a row by name.
The compound API mirrors the shape of your data: a TreeViewItem holds one
TreeViewItemRow for the visible row, and nested TreeViewItem children for its
subtree. Depth, indentation and the connector lines are derived from that nesting, so
there is nothing to keep in sync by hand.
Use it for file explorers, folder pickers, permission trees, category browsers and
nested navigation. For a flat list of options use Select or Combobox, and for a
single expandable section use Accordion.
TreeView is a Create UI Pro component. Install it with a Pro seat.
Installation
Anatomy
Usage
Every TreeViewItem needs a stable id. Pass textValue as well so typeahead, drag
previews and screen readers get the plain-text label.
Examples
Every example is a Pro preview (marked with a Pro badge).
Sizes
size sets the row density. sm gives a 36px row with a 44px indentation step, xs
gives a 24px row with a 28px step. It cascades from the root to every part.
Appearance
solid fills the row with bg-weak on hover and selection. ghost leaves the
background alone and only lifts the label to text-strongest, which suits trees that
sit inside an already busy surface.
Width
fill stretches the row to the container so the highlight spans the full width.
hug shrinks it to the label, so the highlight wraps only the content.
Selection
With selectionMode="multiple" each row gets a checkbox. Selection cascades by default:
checking a parent checks its whole subtree, and a partly checked parent renders the
indeterminate state.
Set selectionCascade={false} to keep rows independent, and showCheckbox to force the
checkbox on or off regardless of the selection mode.
Icons
Pass icon to put a glyph in the leading slot. There is a single leading slot, so an
icon replaces the checkbox on that row, which is how a file tree marks its leaves.
Trailing content
trailing takes a badge, a counter or a spinner. The slot keeps the row height and
grows sideways as needed. For a loading row the design uses
<Spinner variant="neutral-soft" cap="sharp" />, sized sm on an sm tree and xs
on an xs one, so the arc reads as a neutral detail rather than a primary accent.
Without guides
guides={false} keeps the indentation but drops the connector lines, which reads
better for shallow outlines and navigation trees.
Drag and drop
dragAndDrop turns on reordering and dropping rows into other rows. onMove reports
what happened and leaves the data update to you, so the tree stays a controlled view of
your own state. isDropTargetDisabled blocks on drops for rows that cannot contain
children.
Accessibility
TreeView inherits React Aria's tree interaction model. Rows form a single tab stop,
arrow keys walk the visible rows and open or close branches, and typeahead matches
against textValue.
ARIA notes:
- The root renders
role="treegrid"and each row rendersrole="row"witharia-level,aria-posinset,aria-setsize,aria-expandedandaria-selected. - Give the root an
aria-labeloraria-labelledby; without one the tree is unnamed. textValueis what assistive technology announces, so set it whenever the row's children are not plain text.- The connector guides are decorative and marked
aria-hidden. - The chevron is excluded from the tab order and labelled "expand" or "collapse" by
React Aria, so keyboard users toggle a branch with
ArrowRightandArrowLeftrather than tabbing to it. - For the full focus, expansion, and drag interaction model, see the React Aria Tree docs.
Styling
Tailwind override: pass className to the root to size or scroll the tree, and to
TreeViewItemRow to restyle a single row's wrapper.
Data slots and attributes: the component sets these for CSS targeting:
data-slot="tree-view"on the root, withdata-size,data-appearanceanddata-width.data-slot="tree-view-item"on every row, with React Aria'sdata-level,data-expanded,data-has-child-items,data-selected,data-disabled,data-hovered,data-focus-visible,data-dragginganddata-drop-target.data-slot="tree-view-item-content"on the fixed-height box inside the row, the one that folds open and shut.data-slot="tree-view-indent"on each indentation cell, withdata-guide("none","line","elbow","elbow-end").data-slot="tree-view-item-wrapper"on the padded, filled part of the row.data-slot="tree-view-item-drag-handle",-expander,-leading,-icon,-labeland-trailingon the row's parts.data-slot="tree-view-drag-preview"on the card that follows the cursor during a drag.
States: the row wrapper is the only part that changes between states.
The focus ring is drawn with outline-offset: -1px so it sits inside the wrapper and
never changes the row's size.
While a row is being dragged it stays in place under data-dragging, dimmed behind a
dashed frame, and its drag handle and connector guides stay at full strength. The thing
that follows the cursor is a separate surface,
data-slot="tree-view-drag-preview": a lifted white card with a medium border and
shadow-neutral-lg. React Aria gives that preview only the dragged row's text, so the
card shows the label rather than the row's chevron and slots.
Motion: rows fold open over 200ms and shut over 150ms. Collapsing is held back
by exactly one animation, so the closing subtree stays mounted with
data-exiting while it plays and the parent carries data-collapsing so its
chevron turns back straight away. The list itself has no gap; each row owns its
spacing through --tree-view-row-h, which is what lets a folded row measure zero
and the rows below it slide instead of jump. Everything is skipped under
prefers-reduced-motion.
Related Components
- Accordion: use it when you have a handful of independent expandable sections rather than a nested hierarchy.
- Sidebar: use it for the app shell around a tree, not for the hierarchy itself.
- Dropdown Menu: use it for a transient list of actions instead of a persistent hierarchy.
API Reference
TreeView
The root. Extends the React Aria Tree props, including expandedKeys,
defaultExpandedKeys, onExpandedChange, selectedKeys, defaultSelectedKeys,
onSelectionChange, disabledKeys and onAction.
Props
Variants
TreeViewItem
One node. Extends the React Aria TreeItem props, including textValue,
hasChildItems, href and isDisabled. Children are one TreeViewItemRow plus any
nested TreeViewItem elements.
Props
TreeViewItemRow
The visible row: drag handle, indentation cells and the padded wrapper holding the chevron, leading slot, label and trailing slot.
Props
TreeViewItemDragHandle
The grab target that starts a drag. TreeViewItemRow renders it when the root has
showDragHandle; reach for it directly only when you are building a custom row.
Wraps a React Aria Button with slot="drag" and takes no other props.
Props
TreeViewItemExpander
The chevron that opens and closes a branch. Wraps a React Aria Button with
slot="chevron", so React Aria labels it and keeps it out of the tab order. Takes no
other props.
Props
TreeViewItemCheckbox
The leading checkbox. Renders the registry Checkbox with variant="neutral", derives
its checked value (including "indeterminate") from the root's selection state, and
stops its own click from toggling the row twice.
Props
TreeViewItemIcon / TreeViewItemLabel / TreeViewItemTrailing
The leading glyph, the row label and the trailing slot. All three render a <span> and
extend React.ComponentProps<"span">, so any standard span attribute is accepted.
TreeViewItemRow renders the icon and trailing slots for you through its icon and
trailing props.
Every part reads size and appearance from the root context and exposes neither as a
prop of its own.
Types
Key is React Aria's Key (string | number), imported from react-aria-components.