Tree

Table of Contents

Basic Usage

Create a tree using the k-tree component. Set your data by setting the data property.

<k-tree id="myTree"></k-tree>
<script type="module">
  import Tree from '/src/components/Tree.js';
  const tree = document.getElementById('myTree');
  tree.data = {
    name: 'John Doe',
    age: 30,
    active: true,
    hobbies: ['reading', 'coding'],
    address: {
      street: '123 Main St',
      city: 'New York'
    }
  };
</script>

Controlling Initial Depth

Use the depth attribute to control how many levels of branches are open by default. This example shows depth set to 2:

<k-tree id="depthTree" depth="2"></k-tree>
<script type="module">
  import Tree from '/src/components/Tree.js';
  const tree = document.getElementById('depthTree');
  tree.data = {
    company: 'Tech Corp',
    departments: {
      engineering: {
        frontend: ['Alice', 'Bob'],
        backend: ['Charlie', 'Diana']
      },
      sales: {
        team: ['Eve', 'Frank']
      }
    }
  };
</script>

Custom Node Types

You can define custom node types to handle specific data. Create a class that extends TreeNode, implement a static detect method, and override renderLabel() and/or getChildren(), then register it with Tree.addNode().

<k-tree id="customTree"></k-tree>
<script type="module">
  import Tree, { TreeNode } from '/src/components/Tree.js';
  import { html } from '/src/lit-all.min.js';

  class DateNode extends TreeNode {
    renderLabel(){
      const { month, day, year } = this.value;
      return html`<span class="tc-warning">📅 ${month}/${day}/${year}</span>`;
    }

    getChildren(){ return null; }

    static detect = value => {
      return typeof value === 'object'
        && value !== null
        && 'month' in value
        && 'day' in value
        && 'year' in value;
    };
  }

  Tree.addNode(DateNode);

  const tree = document.getElementById('customTree');
  tree.data = {
    event: 'Tech Conference 2025',
    startDate: { month: 10, day: 15, year: 2025 },
    endDate: { month: 10, day: 17, year: 2025 },
    location: 'San Francisco',
    attendees: 250
  };
</script>

Directory Example

This example uses the File System Access API. FileHandleNode and DirHandleNode detect FileSystemFileHandle and FileSystemDirectoryHandle directly — no data transformation needed. DirHandleNode loads its children asynchronously using a reactive internal property, so the tree populates lazily as nodes are opened.

<button id="browseBtn">Browse Directory</button>
<k-tree id="dirTree" depth="1"></k-tree>
<script type="module">
  import Tree, { TreeNode } from '/src/components/Tree.js';
  import { html } from '/src/lit-all.min.js';

  class FileHandleNode extends TreeNode {
    render(){
      const ext = this.value.name.split('.').pop().toLowerCase();
      const iconName = { js: 'code', html: 'code_blocks', css: 'code',
        json: 'file-text', md: 'file-text' }[ext] ?? 'file';
      return html`<span class="d-b"><k-icon name="${iconName}"></k-icon> ${this.value.name}</span>`;
    }
    getChildren(){ return null; }
    static detect = v => v instanceof FileSystemFileHandle;
  }

  class DirHandleNode extends TreeNode {
    static properties = { ...TreeNode.properties, entries: { state: true } };

    connectedCallback(){
      super.connectedCallback();
      (async () => {
        const entries = [];
        for await(const [name, handle] of this.value.entries())
          entries.push([name, handle]);
        this.entries = entries;
      })();
    }

    getChildren(){ return this.entries ?? []; }
    renderIcon(){
      return html`<k-icon name="${this.opened ? 'folder-open' : 'folder'}"></k-icon>`;
    }
    render(){
      const children = this.getChildren();
      return html`
        <div>
          <button class="branch-label no-btn" @click=${this.toggle} aria-expanded="${this.opened}">
            ${this.renderIcon()} ${this.value.name}
          </button>
          ${this.opened ? html`<div class="pl">${children.map(([k, v]) => Tree.renderValue(v, k, this.depth + 1, this.maxDepth))}</div>` : ''}
        </div>
      `;
    }
    static detect = v => v instanceof FileSystemDirectoryHandle;
  }

  Tree.addNode(FileHandleNode, DirHandleNode);

  document.getElementById('browseBtn').onclick = async () => {
    const handle = await showDirectoryPicker();
    document.getElementById('dirTree').data = handle;
  };
</script>

JavaScript Reference

Constructor

Extends ShadowComponent
new Tree()

Creates a new Tree component instance.

Requirements

Properties

data Object | Array

The data to display in the tree. Can be any JavaScript object or array. Nested objects and arrays will be rendered as collapsible branches.

depth Number default: 0

The number of branch levels that should be open by default. A value of 0 means all branches are closed, 1 means the first level is open, 2 means the first two levels are open, etc. This property is reflected as an attribute.

editable Boolean

Whether the tree should be editable. Note: This feature is not yet implemented.

Methods

Tree.addNode(...nodeClasses) Static

Register one or more custom node classes. Node classes should extend TreeNode and implement:

Nodes are checked in order of registration (most recently added first).

The Tree component comes with built-in node types for JavaScript primitives:

Objects and arrays that don't match any registered node type are rendered as collapsible branches using the default TreeNode behavior.