Skip to content
Kappa
Tree View
@dicehub/kappav0.4.2

Tree View

Shows hierarchical data with an accessible standard tree or a windowed tree for 100,000 or more nodes.

Case files

drivaerTest
constant
physicalProperties1.8 KB
polyMesh6 items
system
controlDict2.4 KB
fvSchemes1.2 KB
fvSolution1.6 KB
log.incompressibleFluidRunning
<script setup lang="ts">
import { TreeView, createTreeCollection } from "@dicehub/kappa/components/tree-view";

const caseNode = {
  value: "case",
  label: "drivaerTest",
  children: [
    { value: "constant", label: "constant" },
    { value: "system", label: "system" },
  ],
};
const collection = createTreeCollection({
  rootNode: { value: "ROOT", label: "", children: [caseNode] },
});
</script>

<template>
  <TreeView.Root :collection="collection" :default-expanded-value="['case']">
    <TreeView.Label>Case files</TreeView.Label>
    <TreeView.Tree>
      <TreeView.NodeProvider :node="caseNode" :index-path="[0]">
        <TreeView.Branch>
          <TreeView.BranchControl>
            <TreeView.BranchTrigger>
              <TreeView.BranchIndicator />
            </TreeView.BranchTrigger>
            <TreeView.BranchText>{{ caseNode.label }}</TreeView.BranchText>
          </TreeView.BranchControl>
          <TreeView.BranchContent>
            <TreeView.NodeProvider
              v-for="(node, index) in caseNode.children"
              :key="node.value"
              :node="node"
              :index-path="[0, index]"
            >
              <TreeView.Item><TreeView.ItemText>{{ node.label }}</TreeView.ItemText></TreeView.Item>
            </TreeView.NodeProvider>
          </TreeView.BranchContent>
        </TreeView.Branch>
      </TreeView.NodeProvider>
    </TreeView.Tree>
  </TreeView.Root>
</template>

Choose a Tree

ComponentUseEngine
TreeViewSmall and medium trees with full compound composition.Ark UI Tree View
VirtualTreeLarge and dense trees, including 100,000 or more nodes.Kappa indexed fixed-row engine

Choose the component explicitly. Kappa does not change rendering engines after the tree mounts.

Installation

Both components are part of the main Kappa package. Granular imports keep the large-tree engine separate.

Barrel

import {
  TreeView,
  VirtualTree,
  createTreeCollection,
} from "@dicehub/kappa";

Granular

import {
  TreeView,
  createTreeCollection,
} from "@dicehub/kappa/components/tree-view";
import { VirtualTree } from "@dicehub/kappa/components/virtual-tree";

Standard Tree

Typical part hierarchy
TreeView.Root
├── TreeView.Label
└── TreeView.Tree
    └── TreeView.NodeProvider (recursive)
        ├── TreeView.Branch
        │   ├── TreeView.BranchControl
        │   │   ├── TreeView.BranchTrigger
        │   │   │   └── TreeView.BranchIndicator
        │   │   └── TreeView.BranchText
        │   └── TreeView.BranchContent
        │       └── child NodeProvider parts
        └── TreeView.Item
            └── TreeView.ItemText

TreeView keeps Ark UI controlled state, lazy loading, selection, checking, typeahead, rename, focus, and keyboard behavior.

Render one NodeProvider for each node. Its indexPath must match the collection position.

Large Data

VirtualTree indexes all source nodes once. It renders only the viewport and overscan rows.

No items
<script setup lang="ts">
import { VirtualTree } from "@dicehub/kappa/components/virtual-tree";

const regions = Array.from({ length: 100 }, (_, region) => ({
  value: `region-${region}`,
  label: `Region ${region + 1}`,
  children: Array.from({ length: 1_000 }, (_, cell) => ({
    value: `region-${region}-cell-${cell}`,
    label: `Cell ${region * 1_000 + cell + 1}`,
  })),
}));
</script>

<template>
  <VirtualTree
    :items="regions"
    aria-label="Mesh regions"
    :default-expanded-value="['region-0']"
    height="22rem"
  />
</template>

Examples

Multiple Selection

Use Ark UI selection behavior for a small or medium tree.

Case files

0 selected
drivaerTest
constant
physicalProperties1.8 KB
polyMesh6 items
system
controlDict2.4 KB
fvSchemes1.2 KB
fvSolution1.6 KB
log.incompressibleFluidRunning
<TreeView.Root
  v-model:selected-value="selectedValues"
  :collection="collection"
  selection-mode="multiple"
>
  <!-- Render branches and items with stable values and index paths. -->
</TreeView.Root>

Async Loading

Load each branch independently. A loading request does not lock the rest of the tree.

<script setup lang="ts">
import { VirtualTree } from "@dicehub/kappa/components/virtual-tree";

const items = [{
  value: "results",
  label: "Result snapshots",
  childrenCount: 3,
}];
const loadChildren = async ({ value }: { value: string }) => {
  const response = await fetch(`/api/tree/${value}`);
  return response.json();
};
</script>

<template>
  <VirtualTree
    :items="items"
    :load-children="loadChildren"
    aria-label="Result snapshots"
  />
</template>

Custom Data

Map existing data without copying every source node. Row actions stay separate from tree selection.

Boundary groups
inlet
outlet
walls
<script setup lang="ts">
import { VirtualTree } from "@dicehub/kappa/components/virtual-tree";

const entries = [{
  key: "boundaries",
  name: "Boundary groups",
  entries: [
    { key: "inlet", name: "inlet" },
    { key: "outlet", name: "outlet" },
  ],
}];
</script>

<template>
  <VirtualTree
    :items="entries"
    :node-to-value="(node) => node.key"
    :node-to-string="(node) => node.name"
    :node-to-children="(node) => node.entries"
    aria-label="Mesh boundaries"
  >
    <template #default="{ node }">
      <span>{{ node.name }}</span>
      <button data-kappa-tree-interactive>Inspect</button>
    </template>
  </VirtualTree>
</template>

Performance Contract

  • VirtualTree requires stable, unique string values and a fixed row height.
  • Node lookup is indexed. Adjacent keyboard movement uses visible indexes.
  • Expand and collapse update the visible range. Ordinary scrolling never flattens the source tree.
  • Replace items only when source structure changes. Keep large node objects outside deep Vue reactivity.

Run pnpm --filter @dicehub/kappa benchmark:virtual-tree to measure both 100,000-node shapes on the current machine.

Correspondence

ReferenceCorresponding componentRole in Kappa
KumoNoneNo corresponding component.
Ark UITree ViewBehavior and accessibility base for TreeView.
shadcnNoneNo official Tree View component.

Accessibility

  • Both components use tree and treeitem semantics.
  • VirtualTree adds aria-level, aria-posinset, and aria-setsize because most rows are not mounted.
  • Arrow keys, Home, End, Page Up, Page Down, selection, and typeahead work from the tree focus target.
  • Use data-kappa-tree-interactive on a custom row action. Its click will not select or expand the row.
  • Selection and focus are separate states. Keep both states visible.

Official Ark UI Tree View documentation

API Reference

TreeView.Root

PropTypeDefaultDescription
collectionTreeCollection<T>—Indexed Ark UI tree collection.
expandedValuestring[]—Controlled expanded branch values.
selectedValuestring[]—Controlled selected node values.
selectionMode"single" | "multiple""single"Selection behavior.
loadChildren(details) => Promise<T[]>—Loads branch children before expansion.
typeaheadbooleantrueEnables label typeahead.

VirtualTree

PropTypeDefaultDescription
ariaLabelstring"Tree"Accessible tree name.
defaultExpandedValuestring[][]Initial uncontrolled expanded values.
defaultFocusedValuestring—Initial uncontrolled focused value.
defaultSelectedValuestring[][]Initial uncontrolled selected values.
disabledbooleanfalseDisables tree interaction.
expandOnClickbooleantrueExpands or collapses a branch when its row is clicked.
expandedValuestring[]—Controlled expanded branch values.
focusedValuestring | null—Controlled focused value.
heightCSS height"20rem"Scroll viewport height.
idstringgeneratedStable root and row ID prefix.
indentnumber16Indent for each tree level in pixels.
isNodeDisabled(node) => booleannode.disabledReturns the disabled state.
itemsreadonly T[]—Source tree nodes.
loadChildren(details) => Promise<T[]>—Loads one unloaded branch.
nodeToChildren(node) => T[]node.childrenReturns loaded child nodes.
nodeToChildrenCount(node) => numbernode.childrenCountReports unloaded children.
nodeToString(node) => stringnode.labelReturns the visible and typeahead label.
nodeToValue(node) => stringnode.valueReturns the stable unique node value.
overscannumber8Extra rows rendered around the viewport.
rowHeightnumber28Fixed row height in pixels.
selectedValuestring[]—Controlled selected node values.
selectionMode"single" | "multiple""single"Single or range-capable multiple selection.
typeaheadbooleantrueMoves focus by typed label prefix.

VirtualTree Methods

MethodDescription
scrollToValue(value, align?)Expands loaded ancestors and scrolls the node. Controlled expansion must be applied by the parent.
focus(value)Scrolls to and focuses a node.
expand(value, recursive?)Expands one branch or all loaded descendants.
collapse(value, recursive?)Collapses one branch or all loaded descendants.
getNode(value)Returns the source node from the constant-time index.

Shared Events

EventDescription
expandedChangeRuns after a branch expansion request.
selectionChangeRuns after node selection changes.
focusChangeRuns after the focused value changes.
loadChildrenCompleteRuns after lazy children enter the index.
loadChildrenErrorRuns when lazy loading fails.