Data

Tree

A hierarchical list for browsing nested data such as file systems, with expandable folders, optional guide lines, lazy-loaded children, and single or multi selection.

Preview
01 Installation

Install

Pull Tree from the barrel for everyday use, or the granular path when you want a tighter bundle.

tsxBarrel1 line
import { Tree } from "@brika/clay";
tsxGranular1 line
import { Tree } from "@brika/clay/components/tree";
02 Usage

A minimal example

Drop this into a page. Native HTML attributes pass through to the underlying primitive.

tsxTree.tsx29 lines
'use client';

import { Tree, TreeItem } from '@brika/clay/components/tree';

/** Folders expand on click with single-select, vertical guide lines connect nested items. */
export default function TreeDefaultDemo() {
  return (
    <Tree
      className="w-full max-w-xs"
      showLines
      defaultExpandedIds={['src', 'src/components']}
      defaultSelectedIds={['src/components/button.tsx']}
    >
      <TreeItem nodeId="src" label="src">
        <TreeItem nodeId="src/components" label="components">
          <TreeItem nodeId="src/components/button.tsx" label="button.tsx" />
          <TreeItem nodeId="src/components/card.tsx" label="card.tsx" />
        </TreeItem>
        <TreeItem nodeId="src/index.ts" label="index.ts" />
        <TreeItem nodeId="src/styles.css" label="styles.css" />
      </TreeItem>
      <TreeItem nodeId="public" label="public">
        <TreeItem nodeId="public/favicon.ico" label="favicon.ico" />
      </TreeItem>
      <TreeItem nodeId="package.json" label="package.json" />
      <TreeItem nodeId="README.md" label="README.md" />
    </Tree>
  );
}
03 Examples

Tree, every way

Lazy loading

Folders fetch their children on first expand (Suspense + use) with a spinner while in flight; a failed load (the restricted folder) renders <TreeError/> instead.

Multi-select

Cmd/Ctrl-click (or press Enter) to select several nodes; selection is controlled.

Without Icons

Hide the folder/file glyphs with `showIcons={false}` for a denser list.

04 Accessibility

Accessibility

  • Root carries role="tree"; each node is a role="treeitem" with aria-expanded on folders and aria-selected on selectable nodes.
  • Nested groups use role="group" so assistive tech announces nesting depth.
  • Arrow keys move between visible nodes; Right/Left expand and collapse folders (or step in and out); Home/End jump to the first and last node.
  • Enter or Space selects a node; hold Cmd/Ctrl to extend selection when multiSelect is enabled.
  • A node fetching its children (loading) carries aria-busy while the request is in flight.
05 Tokens 14 theme-overridable

Theme tokens

Every CSS variable Tree reads, with its default and the dotted path you'd write in a ThemeConfig JSON to override it. Set any of these in your theme to retune Tree without touching component code.

Tree tokens

14 tokens
ColorFill, border, and text colors.8
  • --tree-guidecolorvar(--border)
    components.tree.guide

    Color of the vertical guide lines connecting nested items (shown when `showLines`).

  • --tree-iconcolorvar(--muted-foreground)
    components.tree.icon

    Color of the chevron and file glyphs.

  • --tree-folder-iconcolorvar(--primary)
    components.tree.folderIcon

    Color of the folder and folder-open glyphs. Defaults to the brand primary so folder icons pick up the theme accent color automatically.

  • --tree-labelcolorvar(--foreground)
    components.tree.label

    Text color of a resting tree row.

  • --tree-item-hovercolorvar(--accent)
    components.tree.itemHover

    Row background on hover.

  • --tree-selectedcolorcolor-mix(in oklch, var(--primary) 12%, transparent)
    components.tree.selected

    Row background when the node is selected. Defaults to a light tint of the brand primary.

  • --tree-selected-labelcolorvar(--primary)
    components.tree.selectedLabel

    Row text color when the node is selected. Defaults to the brand primary so text and file icon read in the theme accent color.

  • --tree-selected-accentcolorvar(--primary)
    components.tree.selectedAccent

    Color of the vertical accent bar on the leading edge of a selected row. Defaults to the brand primary so it picks up theme coral, navy, etc. automatically.

GeometrySizes, lengths, and corner radii.4
  • --tree-indentsize1.25rem
    components.tree.indent

    Horizontal indentation added per nesting level.

  • --tree-padding-xsizecalc(var(--spacing) * 2)
    components.tree.paddingX

    Inline padding inside the tree.

  • --tree-padding-ysizecalc(var(--spacing) * 1.5)
    components.tree.paddingY

    Block padding inside the tree.

  • --tree-gapsizecalc(var(--spacing) * 2)
    components.tree.gap

    Gap between adjacent children inside the tree.

MotionAnimation duration and easing.2
  • --tree-durationdurationvar(--motion-standard-duration)
    components.tree.duration

    Transition duration for tree state changes.

  • --tree-easingeasingvar(--motion-standard-easing)
    components.tree.easing

    Transition easing for tree state changes.

Override in a theme

Authoring a theme is plain JSON. Drop overrides under components.tree, the names are camelCase versions of the variable suffix.

jsonmy-theme.json12 lines
{
  "id": "my-theme",
  "name": "My Theme",
  "description": "...",
  "accentSwatches": ["#000"],

  "components": {
    "tree": {
      "paddingX": "calc(var(--spacing) * 2)"
    }
  }
}

Then apply with applyTheme(myTheme) (see the theming guide), scope it to a subtree with themeToCssVars(myTheme, mode), or consume any token directly in your own JSX with the matching Tailwind utility (e.g. bg-tree-…) — see the chips on each token row.

06 API reference

API reference

Props specific to Tree. Native HTML attributes pass through to the underlying primitive, see the component source on GitHub for the full type signature.

<Tree />

10 props
  • readonly string[]
    Folder ids open on first render (uncontrolled).
  • readonly string[]
    Selected node ids on first render (uncontrolled).
  • readonly string[]
    Open folder ids (controlled). Pair with onExpandedChange.
  • multiSelectdefault false
    boolean
    Allow more than one node to be selected (Cmd/Ctrl-click extends).
  • (id: string) => void
    Fires once with a node's id the first time it expands (the open transition). Use it to lazy-load that node's children from an API: mark the node lazy so it shows a chevron before its children exist, and toggle its loading prop while the request is in flight.
  • (ids: string[]) => void
    Fires with the next set of open folder ids whenever a folder toggles.
  • (ids: string[]) => void
    Fires with the next set of selected node ids whenever selection changes.
  • readonly string[]
    Selected node ids (controlled). Pair with onSelectedChange.
  • showIconsdefault true
    boolean
    Render folder/file glyphs beside each label. Defaults to true.
  • showLinesdefault false
    boolean
    Draw vertical guide lines connecting nested items. Defaults to false.

<DepthContext />

passthrough

No wrapper-specific props, all attributes pass through to the underlying primitive.

<GuideLines />

1 prop

<TreeContext />

passthrough

No wrapper-specific props, all attributes pass through to the underlying primitive.

<TreeError />

passthrough

No wrapper-specific props, all attributes pass through to the underlying primitive.

<TreeItem />

6 props
  • React.ReactNode
    Row label.
  • string
    Stable identifier used for expansion and selection state.
  • disableddefault false
    boolean
    Block expansion and selection for this node.
  • React.ReactNode
    Override the default folder/file glyph.
  • lazydefault false
    boolean
    Treat this node as an expandable folder even before its children exist, so it shows a chevron and can be opened. Pair with the Tree's onExpand to fetch children on demand. Defaults to false.
  • loadingdefault false
    boolean
    Show a spinner in place of children while they are being fetched. Has an effect only on a lazy (or otherwise childless) open node.

<TreeItemGroup />

2 props

<TreeLoading />

passthrough

No wrapper-specific props, all attributes pass through to the underlying primitive.

<TreePlaceholder />

2 props

<TreeRow />

9 props