Component

Tree

A hierarchical list of expandable nodes. Feed it a nested data model, control expansion and selection (single or multiple), add cascading checkboxes with a select-all, filter with a built-in search box, virtualise long lists, show a loading skeleton, and template the node label or toggle icon.

Nodes, expansion & selection

Birches
Birch
Alder
autumn-notes.txt

selected: [betula] · last click: —

import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { provideIcons } from '@ng-icons/core';
import { phosphorFile, phosphorFolder, phosphorLeaf } from '@ng-icons/phosphor-icons/regular';

import { HbTreeImports, type HbTreeKey, type HbTreeNode } from '@herbst/ui';

@Component({
  selector: 'hb-demo-tree-basic',
  imports: [HbTreeImports],
  viewProviders: [provideIcons({ phosphorFolder, phosphorFile, phosphorLeaf })],
  template: `
    <div class="w-full max-w-xs">
      <hb-tree
        [hbNodes]="nodes"
        [(hbExpanded)]="expanded"
        [(hbSelection)]="selection"
        hbSelectionMode="single"
        hbAriaLabel="Herbst"
        (hbNodeClick)="clicked.set($any($event).label)"
      />

      <p class="mt-3 font-mono text-[12px] text-muted-foreground">
        selected: [{{ selection().join(', ') }}] · last click: {{ clicked() }}
      </p>
    </div>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class HbDemoTreeBasicComponent {
  protected readonly expanded = signal<HbTreeKey[]>(['betulaceae']);
  protected readonly selection = signal<HbTreeKey[]>(['betula']);
  protected readonly clicked = signal('—');

  protected readonly nodes: HbTreeNode[] = [
    {
      key: 'betulaceae',
      label: 'Birches',
      icon: 'phosphorFolder',
      children: [
        { key: 'betula', label: 'Birch', icon: 'phosphorLeaf' },
        { key: 'alnus', label: 'Alder', icon: 'phosphorLeaf' },
      ],
    },
    {
      key: 'fagaceae',
      label: 'Oaks & beeches',
      icon: 'phosphorFolder',
      children: [
        { key: 'quercus', label: 'Oak', icon: 'phosphorLeaf' },
        { key: 'fagus', label: 'Beech', icon: 'phosphorLeaf', disabled: true },
      ],
    },
    { key: 'notes', label: 'autumn-notes.txt', icon: 'phosphorFile', leaf: true },
  ];
}

Cascading checkboxes

Trees
Birch
Oak
Beech
Shrubs
Hazel
Elder

checked: [birch]

Filter & custom templates

selected: []

API reference

hb-tree — data & selection

PropertyDescriptionTypeDefault
[hbNodes] The tree data. Each node is { key, label, icon?, children?, leaf?, disabled?, selectable?, loading?, data? }. HbTreeNode[] []
[(hbExpanded)] The keys of expanded nodes, two-way bound. HbTreeKey[] []
[hbSelectionMode] / [(hbSelection)] Row selection mode and the selected keys (two-way). 'none' | 'single' | 'multiple' / HbTreeKey[] 'single' / []
[hbMetaKeySelection] Multiple mode — require Ctrl/Cmd to add to the selection instead of toggling freely. boolean false

hb-tree — checkboxes & filtering

PropertyDescriptionTypeDefault
[hbCheckable] / [(hbChecked)] Show a checkbox per node and two-way bind the checked keys. boolean / HbTreeKey[] false / []
[hbCheckStrictly] / [hbShowSelectAll] Independent checkboxes (no parent/child cascade), and a select-all header with an indeterminate state. boolean false
[hbShowFilter] / [hbFilter] Show the built-in search box, or drive filtering yourself with a controlled query string. boolean / string false / ""
[hbFilterMode] / [hbFilterPlaceholder] Lenient keeps the matched node’s descendants visible; strict shows only matches and ancestors. Plus the search placeholder. 'lenient' | 'strict' / string 'lenient' / 'Filter…'

hb-tree — rendering & events

PropertyDescriptionTypeDefault
[hbVirtualScroll] / [hbItemSize] / [hbScrollHeight] Virtualise the flattened list with a fixed row height and viewport height. boolean / number / string false / 32 / '16rem'
[hbLoading] / [hbSkeletonRows] Replace the tree with skeleton rows while data loads. boolean / number false / 6
[hbToggleIcon] / [hbIndent] / [hbAriaLabel] The expand icon name, per-level indentation in pixels, and the tree’s accessible label. string / number / string 'phosphorCaretRight' / 16 / ''
(hbNodeExpand) / (hbNodeCollapse) / (hbNodeClick) Emit the node when it is expanded, collapsed, or clicked. HbTreeNode
ng-template[hbTreeNodeContent] / [hbTreeToggle] Template the node label (ctx: $implicit = node, level) and the toggle icon (ctx: $implicit = expanded, node). template