Tree view
A hierarchy a reader opens a level at a time: branches that open and close, and leaves under them. Use it for nested navigation, like this site’s sidebar, or a file tree; when the list is one level deep, a Contained list is enough.
Live preview
Rendered by the component itself from the same generated tokens as the rest of the site.
- components
- ui
- button.tsx
- tree-view.tsx
- parts
- kit-icon.tsx
- docs
- package.json
<TreeView
label="Project files"
nodes={nodes}
selected={selected}
onSelect={setSelected}
/>Installation
Install it with the shadcn CLI, along with everything it uses. New project? Set it up first, as Quick start shows.
npx shadcn@latest add https://www.graphite-ui.com/r/tree-view.jsonimport { TreeView, type TreeNode } from '@/components/ui/tree-view'Anatomy
Rows of one height. Each starts at its level’s indent, then the caret on a branch, the icon, and the node text, 8 apart and 16 clear of the right edge. The selected row takes primary-container and a 4px primary bar at the left.
- ui
- button.tsx
- tag.tsx
- NodesRequiredThe tree's content, as data rather than children. A node with children is the kit's Branch node, which opens and closes; one without is a Leaf. A node with an href is a link, so the tree can be a site's navigation.
- Node textRequiredThe kit's Node text, Body/3, one line, ending in an ellipsis when it runs out of room.
- IconOptionalThe kit's Icon axis. A Branch draws the folder, a Leaf the document, 8 after the caret; on by default, as the kit's Tree view is.
Variants
Size sets the row’s height, 32 or 24, with the text at Body/3 in both. Icons adds the kit’s folder and document and widens the indent step from 16 to 24, as the kit’s spacers do.
- ui
- button.tsx
- tag.tsx
- ui
- button.tsx
- tag.tsx
- ui
- button.tsx
- tag.tsx
- ui
- button.tsx
- tag.tsx
States
Hover climbs one rung of the elevation ladder and pressing two, and the text and glyphs step from on-surface-variant to on-surface. Focus is a ring inside the row. A selected row keeps its fill on hover: the kit’s Selected + Hover step is one the engine does not generate.
- ui
- button.tsx
- tag.tsx
- ui
- button.tsx
- tag.tsx
- ui
- button.tsx
- tag.tsx
- ui
- button.tsx
- tag.tsx
- ui
- button.tsx
- tag.tsx
- ui
- button.tsx
- tag.tsx
API reference
Generated from the contract, versioned with it, and checked by drift-check. This table cannot describe props the component does not have.
| Prop | Type | Default | Notes |
|---|---|---|---|
| label | — | — | Required. The tree's accessible name; a tree with no name is a list of words. |
| nodes | — | — | { id, label, href?, disabled?, children? }[], any depth. The kit draws four levels; deeper levels keep the same step. |
| size | 'sm' | 'xs' | — | The kit's Size, Small (32) and Extra small (24). Small by default. |
| icons | boolean | true | The kit's Icon axis on Tree view and on every node. Also sets the indent step, 24 with icons and 16 without, as the kit's spacers do. |
| selected | — | — | The id of the selected node, the kit's Selected. A site's navigation passes the current page. Its ancestors open. |
| onSelect | — | — | Called with a node's id when it is activated. A link node also navigates, natively. |
| defaultExpanded | — | — | Branch ids open at first. The reader opens and closes them from there. |
Design tokens
Every swatch is live. Change the source color in the header and this table repaints, because it reads the same roles the component does.
elevationHover (elevation-02, the kit's layer-hover-01) and active (elevation-03, layer-active-01). The row at rest has no fill; see Kit parity.primaryThe selected row's 4px inset bar at the left edge (the kit's interactive); the focus ring, 2px inside the row, through the family's focus step; and disabled text and icons, through its disabled-content step.primary-containerThe selected row (the kit's layer-selected-01).on-surface-variantText, caret and icons at rest (the kit's text-secondary and icon-secondary).on-surfaceText, caret and icons on hover, active and selected (the kit's text-primary and icon-primary).textNode text at Body/3 at both sizes.spacingRow heights, the 16 right padding, the indent steps and the 8 gaps.
Usage
The contract's prohibitions, written as the choices you will actually face.
Do
- Use it where the content really nests, and let the reader open only what they need.
- Pass the current page as selected in navigation, so its branch opens and the reader sees where they are.
- Give each node an href when it is a page, so it behaves as a link: middle-click, copy link and history all work.
- Name the tree with label. A screen reader announces it before the first node.
Don’t
- Use it for a flat list. One level is a Contained list, or Navigation Menu for a site header.
- Put buttons or controls inside a node. The tree takes one tab stop and the arrow keys; a second focus target inside it breaks both.
- Rely on indent alone to show the levels. The tree announces them, but a reader skimming needs the carets too.
- Open every branch at once. A tree that shows everything is a long list with extra indentation.
Accessibility
What the component does for you, and what it leaves to you.
- Roles
- The ARIA tree: a
treenamed by label,treeitems witharia-level,aria-expandedon a branch and agroupfor its children. A link node is the treeitem itself, and the current page carriesaria-current. - Keyboard
- One tab stop. Arrow Down and Up move between the nodes that are showing; Right opens a branch or steps into it; Left closes it or steps out to the parent; Home and End go to the ends; Enter and Space activate; a typed letter jumps to the next node that starts with it.
- Focus
- A 2px ring inside the row, through primary’s focus step. The tab stop follows the last node focused, else the selected one, else the first.
- Selection
- The selected node carries
aria-selected. Its ancestors open, so it is always reachable. - Disabled
- A disabled node dims, carries
aria-disabled, and stays reachable by the arrow keys so it is announced, but does not activate. - Contrast
- Text at rest is on-surface-variant on whatever surface holds the tree, and on-surface on the hover, press and selected fills; each pair is measured at the theme’s target.
Figma parity
The kit's Tree view page ships 50 variants across 4 sets: Tree view and Branch node item, which are public, and the private Branch and Leaf spacers the rows indent with.
| Kit axis | Values | Code | How it maps |
|---|---|---|---|
| Node | Branch · Leaf | children | A node with children is a Branch, with the caret; without, a Leaf. |
| Size | Small · Extra small | size | sm and xs: 32 and 24. |
| Icon | True · False | icons | The folder and the document, and the indent step: 24 with icons, 16 without. |
| Open | False · True | defaultExpanded, then the reader | caret-right turns to caret-down. The selected node’s ancestors open. |
| State | Enabled · Hover · Focus · Active · Disabled | disabled | Hover, Focus and Active are pseudo-classes (governance rule 7): elevation-02 and -03, and the inset ring. |
| Selected | False · True | selected | primary-container and a 4px primary bar inside the left edge. |
| Fill | layer-01 | — | No fill at rest. layer-01 is the surface Carbon assumes the tree sits on; transparent reproduces it on whatever holds the tree, rather than drawing a panel on the page. |
| State | Selected + Hover | — | The kit fills state/primary-container-hover, which the engine does not generate. A selected row ignores hover, as Data table’s do. |
| Show Badge indicator | Boolean | — | Defined on the set, but no layer in any variant uses it. Nothing to build. |
| Levels | Level 1 to 4 | any depth | The kit’s spacers stop at four; the code keeps the same step past them. |
