TreeView

Hierarchical data visualization with expandable/collapsible nodes, checkboxes, and selection support

Overview

TreeView provides a hierarchical tree structure for displaying nested data with expand/collapse functionality, selection management, and customisable rendering. Perfect for file systems, folder hierarchies, navigation menus, and any nested data structure.

Key Features

  • Automatic tree building from flat data with parent_id relationships
  • Expand/collapse nodes with smooth animations
  • Single or multi-select node selection
  • Custom node rendering with badges and icons
  • Action buttons on hover for CRUD operations
  • Comprehensive API for programmatic control
  • Event callbacks for all interactions
  • Theme-aware with light/dark mode support

When to Use TreeView

  • File Systems: Display folders and files with hierarchical structure
  • Navigation: Multi-level navigation menus and sitemap structures
  • Organisation: Category hierarchies, taxonomies, org charts
  • Data Management: Hierarchical data browsing and selection

Basic Usage

// Simple hierarchical data
const folders = [
    { id: 1, name: 'Documents', parent_id: null },
    { id: 2, name: 'Work', parent_id: 1 },
    { id: 3, name: 'Personal', parent_id: 1 },
    { id: 4, name: 'Reports', parent_id: 2 }
];

// Create TreeView
const tree = Domma.elements.treeView('#tree', {
    data: folders,
    expandedByDefault: true,
    selectable: true,
    onSelect: (nodeId, node) => {
        console.log('Selected:', node.name);
    }
});

// Control programmatically
tree.expandAll();      // Expand all nodes
tree.collapseAll();    // Collapse all nodes
tree.select(nodeId);   // Select a node
tree.refresh();        // Re-render tree

Getting Started

Add a hierarchical tree view to your page in three simple steps

Step 1: Add Container Element

Create a container div for the tree:

<div id="my-tree"></div>

Step 2: Include Domma

Add Domma CSS and JavaScript to your page:

<!-- CSS -->
<link rel="stylesheet" href="dist/domma.css">
<link rel="stylesheet" href="dist/elements.css">

<!-- JavaScript -->
<script src="dist/domma.min.js"></script>

Step 3: Initialize the TreeView

Call Domma.elements.treeView() with hierarchical data:

// Prepare hierarchical data (flat structure with parent_id)
const folders = [
    { id: 1, name: 'Documents', parent_id: null },
    { id: 2, name: 'Work', parent_id: 1 },
    { id: 3, name: 'Personal', parent_id: 1 },
    { id: 4, name: 'Projects', parent_id: 2 },
    { id: 5, name: 'Reports', parent_id: 2 }
];

// Initialize the tree
const tree = Domma.elements.treeView('#my-tree', {
    data: folders,           // Hierarchical data array
    expandedByDefault: true, // Start with nodes expanded
    selectable: true,        // Allow node selection
    onSelect: (nodeId, node) => {
        console.log('Selected:', node.name);
    },
    onExpand: (nodeId) => {
        console.log('Node expanded:', nodeId);
    }
});

// Programmatic control
tree.expandAll();    // Expand all nodes
tree.collapseAll();  // Collapse all nodes
tree.select(nodeId); // Select a node

Note: The treeview auto-generates the complete tree UI from your data array (no HTML structure required). Data must include id, name, and parent_id fields. Root nodes have parent_id: null. The component automatically builds the hierarchy and handles expand/collapse.

Basic Tree

A simple hierarchical tree with default styling. Click nodes to select them, click the chevron to expand/collapse.

Basic Tree
<div id="tree1"></div>
/**
 * Basic Tree - from public/showcase/elements/treeview.
 */
export function mount(root) {
      // Tree 1: Basic
    const tree1 = E.treeView(root.querySelector('#tree1'), {
        data: [
          {id: 1, name: 'Root', parent_id: null},
          {id: 2, name: 'Child 1', parent_id: 1},
          {id: 3, name: 'Child 2', parent_id: 1},
          {id: 4, name: 'Grandchild', parent_id: 2}
        ]
      });
}
const tree = Domma.elements.treeView('#tree1', {
    data: [
        { id: 1, name: 'Root', parent_id: null },
        { id: 2, name: 'Child 1', parent_id: 1 },
        { id: 3, name: 'Child 2', parent_id: 1 },
        { id: 4, name: 'Grandchild', parent_id: 2 }
    ]
});

With Icons

Add custom icons to tree nodes using the icon property. Uses Domma's icon system.

With Icons
<div id="tree2"></div>
/**
 * With Icons - from public/showcase/elements/treeview.
 */
export function mount(root) {
      // Tree 2: With Icons
    E.treeView(root.querySelector('#tree2'), {
        data: [
          {id: 1, name: 'Documents', parent_id: null, icon: 'folder'},
          {id: 2, name: 'Images', parent_id: null, icon: 'folder'},
          {id: 3, name: 'Report.pdf', parent_id: 1, icon: 'file-text'},
          {id: 4, name: 'Photo.jpg', parent_id: 2, icon: 'image'}
        ]
      });
}
Domma.elements.treeView('#tree2', {
    data: [
        { id: 1, name: 'Documents', parent_id: null, icon: 'folder' },
        { id: 2, name: 'Images', parent_id: null, icon: 'folder' },
        { id: 3, name: 'Report.pdf', parent_id: 1, icon: 'file-text' },
        { id: 4, name: 'Photo.jpg', parent_id: 2, icon: 'image' }
    ]
});

With Badges

Display counts or status indicators using the getBadge callback. Perfect for showing item counts.

With Badges
<div id="tree3"></div>
/**
 * With Badges - from public/showcase/elements/treeview.
 */
export function mount(root) {
      // Tree 3: With Badges
    E.treeView(root.querySelector('#tree3'), {
        data: [
          {id: 1, name: 'Projects', parent_id: null, count: 12},
          {id: 2, name: 'Work', parent_id: 1, count: 5},
          {id: 3, name: 'Personal', parent_id: 1, count: 7},
          {id: 4, name: 'Archive', parent_id: null, count: 25}
        ],
        getBadge: (node) => node.count || null
      });
}
Domma.elements.treeView('#tree3', {
    data: [
        { id: 1, name: 'Projects', parent_id: null, count: 12 },
        { id: 2, name: 'Work', parent_id: 1, count: 5 },
        { id: 3, name: 'Personal', parent_id: 1, count: 7 },
        { id: 4, name: 'Archive', parent_id: null, count: 25 }
    ],
    getBadge: (node) => node.count || null
});

With Action Buttons

Add action buttons that appear on hover. Perfect for CRUD operations like add, edit, and delete.

With Action Buttons
<div id="tree4"></div>
/**
 * With Action Buttons - from public/showcase/elements/treeview.
 */
export function mount(root) {
      // Tree 4: With Actions
    E.treeView(root.querySelector('#tree4'), {
        data: [
          {id: 1, name: 'Folder 1', parent_id: null},
          {id: 2, name: 'Folder 2', parent_id: null},
          {id: 3, name: 'Subfolder', parent_id: 1}
        ],
        actions: [
          {name: 'add', icon: 'plus', tooltip: 'Add subfolder'},
          {name: 'edit', icon: 'edit', tooltip: 'Edit'},
          {name: 'delete', icon: 'trash', tooltip: 'Delete'}
        ],
        onAction: (action, nodeId, node) => {
          E.toast(`${action}: ${node.name}`, {type: 'info'});
        }
      });
}
Domma.elements.treeView('#tree4', {
    data: [
        { id: 1, name: 'Folder 1', parent_id: null },
        { id: 2, name: 'Folder 2', parent_id: null },
        { id: 3, name: 'Subfolder', parent_id: 1 }
    ],
    actions: [
        { name: 'add', icon: 'plus', tooltip: 'Add subfolder' },
        { name: 'edit', icon: 'edit', tooltip: 'Edit' },
        { name: 'delete', icon: 'trash', tooltip: 'Delete' }
    ],
    onAction: (action, nodeId, node) => {
        Domma.elements.toast(`${action}: ${node.name}`, { type: 'info' });
    }
});

Custom Node Rendering

Fully customise node appearance using the renderNode callback. Add file sizes, metadata, or any custom HTML.

Custom Node Rendering
<div id="tree5"></div>
/**
 * Custom Node Rendering - from public/showcase/elements/treeview.
 */
export function mount(root) {
      // Tree 5: Custom Rendering
    E.treeView(root.querySelector('#tree5'), {
        data: [
          {id: 1, name: 'index.js', parent_id: null, icon: 'file-text', size: '2.4 KB'},
          {id: 2, name: 'package.json', parent_id: null, icon: 'package', size: '1.2 KB'},
          {id: 3, name: 'src', parent_id: null, icon: 'folder', size: ''},
          {id: 4, name: 'utils.js', parent_id: 3, icon: 'file-text', size: '3.1 KB'}
        ],
        renderNode: (node, depth, state) => {
          const iconColor = node.icon === 'folder' ? '#f59e0b' : '#6b7280';
          return `
                    <span data-icon="${node.icon}" style="color: ${iconColor};"></span>
                    <span style="flex: 1;">${node.name}</span>
                    ${node.size ? `<span style="color: var(--dm-slate-500); font-size: 0.75rem;">${node.size}</span>` : ''}
                `;
        }
      });
}
Domma.elements.treeView('#tree5', {
    data: [
        { id: 1, name: 'index.js', parent_id: null, icon: 'file-text', size: '2.4 KB' },
        { id: 2, name: 'package.json', parent_id: null, icon: 'package', size: '1.2 KB' },
        { id: 3, name: 'src', parent_id: null, icon: 'folder', size: '' },
        { id: 4, name: 'utils.js', parent_id: 3, icon: 'file-text', size: '3.1 KB' }
    ],
    renderNode: (node, depth, state) => {
        const iconColor = node.icon === 'folder' ? '#f59e0b' : '#6b7280';
        return `
            <span data-icon="${node.icon}" style="color: ${iconColor};"></span>
            <span style="flex: 1;">${node.name}</span>
            ${node.size ? `<span style="color: var(--dm-slate-500); font-size: 0.75rem;">${node.size}</span>` : ''}
        `;
    }
});

Event Handling

React to user interactions with event callbacks. Monitor selections, expansions, and custom actions.

Event Handling
Event Log:
Click tree nodes to see events...
<div id="tree6"></div>
<strong>Event Log:</strong>
<div id="eventLog" style="font-family: monospace; font-size: 0.875rem; margin-top: 0.5rem; max-height: 150px; overflow-y: auto;">
    <div data-each="entries.value key=id">
        <div data-bind-text="text"></div>
    </div>
    Click tree nodes to see events...
</div>
/**
 * Event Handling - from public/showcase/elements/treeview.
 *
 * The event log is a bound list (data-each), newest first.
 */
export function mount(root) {
    // Tree 6: Events
    const entries = M.observable([]);
    let seq = 0;
    const handle = M.applyBindings({entries}, root);

    function logEvent(message) {
        const time = D().format('HH:mm:ss');
        entries.value = [{id: ++seq, text: `[${time}] ${message}`}, ...entries.value];
    }
    const tree = E.treeView(root.querySelector('#tree6'), {
        data: [
            {id: 1, name: 'Node 1', parent_id: null},
            {id: 2, name: 'Node 2', parent_id: null},
            {id: 3, name: 'Child', parent_id: 1}
        ],
        onSelect: (nodeId, node) => {
            logEvent(`Selected: ${node.name}`);
        },
        onExpand: (nodeId, node) => {
            logEvent(`Expanded: ${node.name}`);
        },
        onCollapse: (nodeId, node) => {
            logEvent(`Collapsed: ${node.name}`);
        }
    });

    return () => {
        handle.dispose();
        tree?.destroy?.();
    };
}
Domma.elements.treeView('#tree6', {
    data: [
        { id: 1, name: 'Node 1', parent_id: null },
        { id: 2, name: 'Node 2', parent_id: null },
        { id: 3, name: 'Child', parent_id: 1 }
    ],
    onSelect: (nodeId, node) => {
        console.log('Selected:', node.name);
    },
    onExpand: (nodeId, node) => {
        console.log('Expanded:', node.name);
    },
    onCollapse: (nodeId, node) => {
        console.log('Collapsed:', node.name);
    }
});

Programmatic Control

Control the tree programmatically using the comprehensive API. Expand, collapse, select, and navigate nodes.

Programmatic Control
<button type="button" class="btn btn-sm btn-primary" data-action="1">Expand All</button>
<button type="button" class="btn btn-sm btn-secondary" data-action="2">Collapse All</button>
<button type="button" class="btn btn-sm btn-success" data-action="3">Select Node 2</button>
<button type="button" class="btn btn-sm btn-info" data-action="4">Deselect All</button>
<button type="button" class="btn btn-sm btn-warning" data-action="5">Get Selected</button>
<div id="tree7"></div>
/**
 * Programmatic Control - from public/showcase/elements/treeview.
 */
export function mount(root) {
      // Tree 7: API Methods
    window.tree7 = E.treeView(root.querySelector('#tree7'), {
        data: [
          {id: 1, name: 'Node 1', parent_id: null},
          {id: 2, name: 'Node 2', parent_id: null},
          {id: 3, name: 'Node 3', parent_id: null},
          {id: 4, name: 'Child 1-1', parent_id: 1},
          {id: 5, name: 'Child 2-1', parent_id: 2},
          {id: 6, name: 'Grandchild', parent_id: 4}
        ]
      });
    function showSelected() {
        const selected = tree7.getSelected();
        if (selected.length > 0) {
          E.toast(`Selected IDs: ${selected.join(', ')}`, {type: 'success'});
        } else {
          E.toast('No nodes selected', {type: 'info'});
        }
      }

    // Handlers the old page had inline (onclick=…), wired here.
    $(root).find('[data-action="1"]').on('click', function (event) {
        tree7.expandAll();
    });
    $(root).find('[data-action="2"]').on('click', function (event) {
        tree7.collapseAll();
    });
    $(root).find('[data-action="3"]').on('click', function (event) {
        tree7.select(2);
    });
    $(root).find('[data-action="4"]').on('click', function (event) {
        tree7.deselectAll();
    });
    $(root).find('[data-action="5"]').on('click', function (event) {
        showSelected();
    });
}
const tree = Domma.elements.treeView('#tree', { data });

// Expansion
tree.expandAll();           // Expand all nodes
tree.collapseAll();         // Collapse all nodes
tree.toggle(nodeId);        // Toggle specific node
tree.expand(nodeId);        // Expand specific node
tree.collapse(nodeId);      // Collapse specific node

// Selection
tree.select(nodeId);        // Select a node
tree.deselect(nodeId);      // Deselect a node
tree.selectAll();           // Select all (multiSelect only)
tree.deselectAll();         // Clear all selections
tree.getSelected();         // Get array of selected IDs

// Navigation
tree.getParent(nodeId);     // Get parent node
tree.getChildren(nodeId);   // Get child nodes
tree.getAncestors(nodeId);  // Get path to root
tree.getDescendants(nodeId);// Get all descendants
tree.getLevel(nodeId);      // Get depth level

// Data
tree.setData(newData);      // Replace tree data
tree.refresh();             // Re-render tree
tree.updateNode(id, data);  // Update specific node
tree.getData();             // Get hierarchical data
tree.getFlatData();         // Get flat array

// Utilities
tree.find(predicate);       // Find first matching node
tree.filter(predicate);     // Find all matching nodes
tree.scrollToNode(nodeId);  // Scroll into view

Configuration Options

Data Configuration

Option Type Default Description
data Array [] Flat or hierarchical array of nodes
idKey String 'id' Property name for node ID
parentKey String 'parent_id' Property name for parent reference
labelKey String 'name' Property name for node label
iconKey String 'icon' Property name for icon name

Behaviour Options

Option Type Default Description
expandedByDefault Boolean true Expand all nodes initially
selectable Boolean true Enable node selection
multiSelect Boolean false Allow multiple selected nodes
indentSize Number 1.5 Indentation per level (rem)

Callback Options

Option Parameters Description
renderNode (node, depth, state) Custom node rendering function
getBadge (node) Return badge content for node
onSelect (nodeId, node, event) Called when node is selected
onDeselect (nodeId, node, event) Called when node is deselected
onExpand (nodeId, node, event) Called when node expands
onCollapse (nodeId, node, event) Called when node collapses
onAction (action, nodeId, node, event) Called when action button clicked

CSS Customisation

Override these CSS variables to customise TreeView appearance and match your design system.

Variable Default Controls
--dm-primary var(--dm-blue-600) Selected node colour
--dm-hover-bg var(--dm-slate-100) Node hover background
--dm-border var(--dm-slate-300) Tree border colour
--dm-text var(--dm-slate-800) Node text colour

Example Override

:root {
    /* Highlight selected nodes */
    --dm-primary: #059669;
}

Full CSS Customisation Cheat-Sheet →