Components

Tree

Accessible hierarchical navigation and selection with lazy loading, propagation, typeahead, and virtualization.

Tree renders nested data as one accessible tree view. Selection and expansion use stable item keys, so the data may be replaced after an API request without losing state.

The component owns the flat role="tree" structure, roving focus, keyboard navigation, ARIA hierarchy metadata, and optional virtualization. The application keeps control of data fetching and item content.

Anatomy

  • #item: Replaces the generated content inside every row while retaining tree semantics and selection styling.
  • #item-[name]: Replaces generated content for items whose slot accessor resolves to that name.
  • #toggle: Branch expand/collapse control.
  • #indicator: Selection indicator, including indeterminate state.
  • #icon: File, folder, or domain-specific icon.
  • #label: Item content, including optional supporting text.
  • #trailing: Metadata or actions at the end of a row.
  • #loading: Loading row shown under an expanded lazy branch.
  • #empty: Empty state when no items are available.

Every item slot receives item, normalized node state, node actions, toggleProps, and the tree controller.

Usage

Selected: tree

Examples

Lazy children

Mark unloaded branches with hasChildren. When a branch expands, fetch its children, replace the corresponding item, and remove its key from loadingKeys.

Tree does not cache or fetch data. This keeps request deduplication, retries, authorization, and cache policy in the application.

Multiple selection and propagation

Use selectionPropagation="both" when selecting a branch should select its descendants and fully selected descendants should select their ancestors.

<template>
  <Tree
    v-model="permissions"
    :items="permissionItems"
    item-key="id"
    selection-mode="multiple"
    selection-propagation="both"
  >
    <template #indicator="{ node }">
      <span>{{ node.isIndeterminate ? "-" : node.isSelected ? "x" : "" }}</span>
    </template>
  </Tree>
</template>

Propagation modes are none, down, up, and both. Disabled and non-selectable items are excluded.

For leaf-only selection, resolve selectableKey from the item rather than adding another selection mode:

<Tree
  :items="items"
  item-key="id"
  :selectable-key="item => item.type === 'file'"
/>

Icons and row actions

Use the granular slots for content that should keep the built-in row behavior. Use #item only when replacing the complete row.

<template>
  <Tree :items="items" item-key="id">
    <template #icon="{ item, node }">
      <FolderOpenIcon v-if="item.type === 'folder' && node.isExpanded" />
      <FolderIcon v-else-if="item.type === 'folder'" />
      <FileIcon v-else />
    </template>

    <template #trailing="{ item }">
      <button type="button" data-akaza-tree-ignore @click="rename(item)">Rename</button>
    </template>
  </Tree>
</template>

Interactive controls in a row do not activate or select the node. Add data-akaza-tree-ignore to a custom interactive wrapper that is not a native button, link, input, select, textarea, or editable element.

The data contract has no built-in description field. Render a title, description, or any other item content through #label:

<Tree :items="items" item-key="id">
  <template #label="{ item }">
    <span class="grid">
      <span>{{ item.title }}</span>
      <span class="text-sm text-neutral-500">{{ item.description }}</span>
    </span>
  </template>
</Tree>

Row expansion

Clicking a branch row toggles it by default. Native controls and elements marked with data-akaza-tree-ignore do not toggle the branch. Restrict expansion to the generated toggle button when row clicks perform another action:

<Tree :expand-on-click="false" />

Selection highlight

Selection semantics always stay on the complete treeitem. selectionHighlight only controls the visual highlight boundary:

<Tree selection-highlight="full" />
<Tree selection-highlight="indented" />
<Tree selection-highlight="label" />
  • full spans the available row width.
  • indented starts at the node depth. This is the default.
  • label follows the label content.

Style all three modes with ui.selection. Its data-akaza-selection-highlight value identifies the active boundary.

Expansion motion

Children fade and move a few pixels as branches open and close. Existing rows also move into their new position. Disable this behavior when another animation system owns the rows:

<Tree :transition="false" />

Motion is disabled automatically for virtualized trees and reduced to an effectively immediate transition when the user prefers reduced motion.

Activation and detail loading

activate runs on row click, touch, or Enter. Use it to load a detail panel independently from branch expansion.

<template>
  <Tree
    :items="items"
    item-key="id"
    @activate="(item, details) => openDetails(item, details.reason)"
  />
</template>

Virtualization

Virtualization keeps only the visible rows mounted. Set a fixed row height and viewport height.

<template>
  <Tree
    :items="largeTree"
    item-key="id"
    :virtualize="{ height: 320, itemHeight: 36, overscan: 6 }"
  />
</template>

Keep custom rows and loading content compatible with itemHeight; variable-height virtualization is not supported.

API Reference

Models

ModelTypeDefaultDescription
v-modelTreeKey | TreeKey[] | nullnullSelected key in single mode or selected keys in multiple mode.
v-model:expandedTreeKey[]defaultExpandedExpanded branch keys.

Props

PropTypeDefaultDescription
itemsreadonly T[]requiredNested tree data.
itemKeykeyof T | (item) => TreeKeyrequiredStable, globally unique item key.
labelKeykeyof T | (item) => unknown"label"Label accessor.
childrenKeykeyof T | (item) => T[]"children"Child collection accessor.
disabledKeykeyof T | (item) => unknown"disabled"Disabled state accessor.
selectableKeykeyof T | (item) => unknown"selectable"Selectable state accessor.
defaultExpandedKeykeyof T | (item) => unknown"defaultExpanded"Initial expansion accessor.
slotKeykeyof T | (item) => unknown"slot"Dynamic item slot accessor.
textValue(item: T) => stringlabel textText used by typeahead.
hasChildren(item: T) => boolean-Marks a branch before its children are loaded.
defaultValueTreeSelectionnullInitial uncontrolled selection.
defaultExpandedTreeKey[]item defaultsInitial uncontrolled expansion.
loadingKeysreadonly TreeKey[][]Branches currently loading children.
selectionMode"none" | "single" | "multiple""single"Selection mode.
selectionBehavior"replace" | "toggle""toggle"Multiple-selection behavior without a modifier key.
selectionPropagation"none" | "up" | "down" | "both""none"Branch selection propagation.
selectionHighlight"full" | "indented" | "label""indented"Visual boundary used by ui.selection; ARIA selection remains on the complete item.
selectOnFocusbooleanfalseSelects a node when roving focus reaches it.
expandOnClickbooleantrueToggles a branch when its non-interactive row content is clicked. Toggle buttons always work.
disabledbooleanfalseDisables the tree.
readOnlybooleanfalseBlocks selection changes while preserving navigation and expansion.
typeaheadbooleantrueEnables printable-character navigation.
transitionbooleantrueEnables built-in branch row motion. Ignored during virtualization.
indentnumber16Indentation in pixels for each nested level.
orientation"vertical" | "horizontal""vertical"Navigation orientation.
dir"ltr" | "rtl""ltr"Text direction and horizontal Arrow behavior.
virtualizeboolean | TreeVirtualOptionsfalseEnables fixed-height row virtualization.
ariaLabelstring"Tree"Accessible tree label.
ariaLabelledbystring-Id of an accessible label.
ariaDescribedbystring-Id of supporting text.
labelsTreeLabels<T>-Expand, collapse, loading, and empty labels.
asstring | Component"div"Root element or component.
uiTreeUi-Classes for structural parts.

TreeVirtualOptions accepts height, itemHeight, and overscan numbers.

Emits

EventPayloadDescription
selection-change(value, details)Fired before selection updates.
expanded-change(keys, details)Fired before expansion updates. details.expanded reports the requested branch state.
focus-change(key, details)Fired before the roving focus key changes.
activate(item, details)Fired on row click, touch, or Enter.

Event details include key, item, reason, the original event when available, and cancel(). Calling cancel() in a change event prevents its model update.

Slots

SlotPropsDescription
itemitem, node, actions, toggleProps, treeReplaces generated content inside every row.
item-[name]same as itemContent replacement selected by an item's slot value.
toggleitem, node, actions, toggleProps, treeToggle content inside the generated button.
indicatoritem, node, actions, toggleProps, treeSelection indicator.
iconitem, node, actions, toggleProps, treeItem icon.
labelitem, node, actions, toggleProps, treeItem content. May contain a title and supporting text.
trailingitem, node, actions, toggleProps, treeTrailing content or controls.
loadingitem, node, actions, toggleProps, treeExpanded lazy-branch loading content.
emptytreeEmpty state.

node exposes key, parent, depth, sibling position, label, and branch, expansion, selection, indeterminate, focus, disabled, selectable, and loading state.

Exposed Methods

MethodDescription
getNode(key)Returns normalized item state.
getItem(key)Returns the source item.
focus(key?)Focuses a visible enabled item.
select(key)Selects an item.
toggleSelection(key)Toggles item selection.
expand(key) / collapse(key)Changes one branch.
toggleExpanded(key)Toggles one branch.
expandAll() / collapseAll()Changes all enabled branches.
activate(key)Emits item activation.

The tree controller available to slots exposes the same methods plus reactive visible nodes, selected items, models, and focused key.

Item Shape

Default accessors recognize these keys. Accessor props support any data shape.

KeyTypeDescription
idstring | numberStable key when item-key="id".
labelstringVisible label and default typeahead text.
childrenTreeItem[]Nested items.
disabledbooleanDisables interaction.
selectablebooleanExcludes the item from selection while keeping navigation.
defaultExpandedbooleanExpands the branch initially in uncontrolled mode.
slotstringSelects #item-[name].

UI Options

KeyDescription
rootComponent root.
viewportScrollable element with role="tree".
virtualWrapperVirtual row sizing wrapper.
itemFocusable role="treeitem".
rowVisible row content.
indentStructural indentation spacer.
contentIndented content from the node depth onward.
selectionActive full, indented, or label highlight boundary. Use it for selection, hover, and focus styles.
toggleBranch toggle. Its dimensions are reused by the hidden leaf spacer.
leafSpacerHidden leaf alignment spacer outside the indented highlight boundary.
toggleIconDefault rotating chevron.
indicatorSelection indicator wrapper.
iconIcon wrapper.
textLabel wrapper.
labelItem content.
trailingTrailing content.
loadingLazy branch loading row.
emptyEmpty state.

Styling Hooks

UI keyCSS classData attrs
rootakaza-treedata-akaza-state, data-akaza-orientation, data-akaza-disabled, data-akaza-readonly, data-akaza-selection-highlight, data-akaza-transition
viewportakaza-tree-viewporttree ARIA attrs, virtual height style
virtualWrapperakaza-tree-virtual-wrappervirtual transform/height style
itemakaza-tree-itemdata-akaza-state, data-akaza-selected, data-akaza-indeterminate, data-akaza-focused, data-akaza-disabled, data-akaza-selectable, data-akaza-loading, data-akaza-depth, hierarchy ARIA attrs
rowakaza-tree-rowItem state attrs plus data-akaza-selection-highlight
indentakaza-tree-indentWidth uses --akaza-tree-indent-offset
contentakaza-tree-contentStarts after the indentation spacer
selectionakaza-tree-selectiondata-akaza-selected, data-akaza-indeterminate, data-akaza-focused, data-akaza-selection-highlight
toggleakaza-tree-togglearia-expanded, native disabled; also sizes the hidden leaf spacer
leafSpacerakaza-tree-toggle-spaceraria-hidden="true"
toggleIconakaza-tree-toggle-icondata-akaza-state="expanded | collapsed"
indicatorakaza-tree-indicator-
iconakaza-tree-iconaria-hidden="true"
textakaza-tree-text-
labelakaza-tree-label-
trailingakaza-tree-trailing-
loadingakaza-tree-loadingrole="status"
emptyakaza-tree-empty-

Plain class applies to the root. Use ui.viewport, ui.item, and the granular part keys for generated content. Items expose --akaza-tree-depth and the computed --akaza-tree-indent-offset.

Keyboard

KeyBehavior
ArrowDown / ArrowUpMove through visible items in a vertical tree.
ArrowRightExpand a collapsed branch, then move to its first child. In RTL, ArrowLeft does this.
ArrowLeftCollapse an expanded branch, then move to its parent. In RTL, ArrowRight does this.
Home / EndMove to the first/last enabled visible item.
*Expand all enabled branches at the current level.
SpaceSelect or toggle the focused item.
EnterSelect and activate the focused item.
Shift + movement Arrow or Home / EndExtend a multiple-selection range from its anchor.
Ctrl+A / Cmd+ASelect all enabled items in multiple mode.
printable charactersMove to the next visible label matching the typed prefix.

Horizontal orientation maps movement to Left/Right and expansion to Down/Up. One visible enabled item participates in the page tab order.

In multiple mode, Shift + click selects the visible range from the last selection anchor. Ctrl / Cmd + Shift + click adds that range to the current selection.