Acronis UIKit
Components

TreeItem

One flat row of a tree or nested-list UI — chevron, checkbox, icon, title, and a trailing extras slot.

Usage

import { TreeItem } from '@acronis-platform/ui-react';

TreeItem is a single row of a tree or nested list: an optional expand chevron, an optional Checkbox, an optional leading icon, the row title, and an optional trailing extras slot for children. Every slot is gated by its own boolean, mirroring the Figma component's properties — isExpandable (on by default), hasCheckbox, hasIcon, hasExtras (on by default). A separate expanded boolean rotates the chevron to point down.

It is deliberately one row and nothing more. It renders no nested list, owns no expand state, and selects nothing: you compose the rows, indent them, hold the open/selected state, and supply the ARIA tree semantics. That is the same boundary Breadcrumb draws — it ships the trail's primitives without owning the trail.

isExpandable and expanded are artwork, not behavior. The first draws the chevron; the second rotates it a quarter turn to point down. Nothing expands when you click it, and there is no change event — the Figma node's expandable slot has no interaction either, so the consumer holds the open state and renders the child level itself. Pass expanded from the same boolean that feeds your aria-expanded: the chevron shows the state to sighted users, aria-expanded announces it, and setting only one leaves them disagreeing.

selected is a look you drive. The row never selects itself on click; it only reflects the flag and exposes data-selected. Figma's state=active swatch paints the same highlighted fill, which is why active maps to this prop rather than to CSS :active.

The row is polymorphic via Base UI's useRender (the render prop), and that is how you give it real tree semantics. No role="treeitem" and no tabIndex are forced: a row is only a valid tree item inside a role="tree" owner, and the role without aria-expanded / aria-level / aria-selected and a roving tabindex would make the tree invalid rather than accessible. The checkbox is never unnamed — its aria-label defaults to title, and checkboxProps is forwarded verbatim so you control checked / onCheckedChange exactly as on a standalone Checkbox.

@acronis-platform/tokens-pd ships no Tree component tier — the Figma node's variables still sit in the pre-next-gen componentLegacy/tree/* namespace. So the row's colors come from the semantic tier (--ui-text-on-surface-primary, --ui-glyph-on-surface-primary, --ui-background-surface-hover / --ui-background-surface-active, --ui-focus-primary) and its layout numbers are plain utilities matching the design's pixel values. Both get re-pointed when a --ui-tree-item-* tier lands.

Examples

A real tree — the roles, indentation, expand state, and selection are all yours:

const [open, setOpen] = useState(true);
const [selected, setSelected] = useState('cloud');

<ul role="tree" aria-label="Workloads">
  <li role="treeitem" aria-expanded={open} aria-level={1}>
    <TreeItem
      expanded={open}
      hasIcon
      icon={<FolderIcon size={16} />}
      title="All workloads"
      tabIndex={0}
      onClick={() => setOpen((value) => !value)}
      onKeyDown={(event) => {
        if (event.key === 'Enter' || event.key === ' ') {
          event.preventDefault();
          setOpen((value) => !value);
        }
      }}
    >
      <Tag variant="info">24</Tag>
    </TreeItem>

    {open && (
      <ul role="group" className="ps-4">
        <li role="treeitem" aria-level={2} aria-selected={selected === 'cloud'}>
          <TreeItem
            isExpandable={false}
            hasIcon
            icon={<FolderIcon size={16} />}
            title="Cloud applications"
            selected={selected === 'cloud'}
            tabIndex={-1}
            onClick={() => setSelected('cloud')}
            onKeyDown={(event) => {
              if (event.key === 'Enter' || event.key === ' ') {
                event.preventDefault();
                setSelected('cloud');
              }
            }}
          />
        </li>
      </ul>
    )}
  </li>
</ul>;

A multi-select row — the checkbox is controlled through checkboxProps:

<TreeItem
  hasCheckbox
  hasIcon
  title="Machines with agents"
  checkboxProps={{
    checked,
    onCheckedChange: (value) => setChecked(Boolean(value)),
  }}
/>

A row with a trailing action, and a leaf row with no chevron:

<TreeItem hasIcon title="Backups">
  <ButtonIcon aria-label="Rename">
    <PencilIcon size={16} />
  </ButtonIcon>
</TreeItem>

<TreeItem isExpandable={false} hasIcon title="readme.md" />

hasExtras={false} removes the trailing slot and its children from the DOM — a button in there does not exist, so it cannot be tabbed to:

<TreeItem hasExtras={false} title="Workloads">
  <Tag variant="info">12</Tag> {/* not rendered */}
</TreeItem>

API Reference

Prop

Type

Edit on GitHub

On this page