<lightning-tree-grid> components in modern Lightning Web Components (LWC).
Standard Salesforce record lists display flat tables, making it difficult for users to visualize nested business relationships. Complex enterprise hierarchies—such as parent-child account trees, multi-level product bundles, or department role trees—require an intuitive, expandable node structure. Building custom tree views helps users navigate data relationships quickly without opening dozens of separate browser tabs.
1. Understanding the Recursive Tree Data Model
Hierarchical tree structures rely on a self-referencing wrapper model where every node can hold zero or more child nodes of the exact same structure:
- Tree Node Wrapper: A dedicated Apex class containing properties for the record label, unique record ID, expanded state, and a nested
List<TreeNode>of children. - Parent-Child Relationship Binding: The Apex controller queries database records (e.g., Accounts with
ParentId), groups children under their respective parents using Maps, and populates the root nodes. - Visual Presentation Layer: Renders nested unordered lists (
<ul>/<li>) in Visualforce or passes structured JSON directly into LWC component grids.
- Data Structure: Recursive Apex Wrapper (
TreeNodecontainingList<TreeNode> children). - Visualforce Engine: Nested
<apex:repeat>tags formatted with CSS tree connectors. - Modern LWC Component:
<lightning-tree-grid>or<lightning-tree>with nested JSON items. - Security Standard: Enforce
with sharingand user-mode database operations (WITH USER_MODE).
2. Step-by-Step Visualforce & Apex Implementation
This controller builds an Account hierarchy by querying parent and child accounts.
public with sharing class TreeViewController {
public class TreeNode {
public String id { get; set; }
public String name { get; set; }
public List<TreeNode> children { get; set; }
public TreeNode(String recordId, String label) {
this.id = recordId;
this.name = label;
this.children = new List<TreeNode>();
}
}
public List<TreeNode> treeNodes { get; set; }
public TreeViewController() {
loadAccountHierarchy();
}
private void loadAccountHierarchy() {
treeNodes = new List<TreeNode>();
// Query Top-Level Parent Accounts and their direct children
List<Account> accounts = [
SELECT Id, Name, ParentId,
(SELECT Id, Name FROM ChildAccounts LIMIT 20)
FROM Account
WHERE ParentId = NULL
WITH USER_MODE
LIMIT 10
];
for (Account parentAcc : accounts) {
TreeNode parentNode = new TreeNode(parentAcc.Id, parentAcc.Name);
for (Account childAcc : parentAcc.ChildAccounts) {
TreeNode childNode = new TreeNode(childAcc.Id, childAcc.Name);
parentNode.children.add(childNode);
}
treeNodes.add(parentNode);
}
}
}
<apex:page controller="TreeViewController" lightningStylesheets="true">
<style>
.ap-tree-container {
padding: 20px;
background: #ffffff;
border: 1px solid #d8dde6;
border-radius: 6px;
}
ul.ap-tree, ul.ap-tree ul {
list-style-type: none;
margin: 0;
padding-left: 24px;
}
ul.ap-tree li {
margin: 8px 0;
position: relative;
}
ul.ap-tree li::before {
content: "";
position: absolute;
top: 12px;
left: -16px;
width: 12px;
height: 1px;
background-color: #a8b7c7;
}
ul.ap-tree > li::before {
display: none;
}
.ap-tree-node-badge {
display: inline-block;
background-color: #f3f2f2;
border: 1px solid #dddbda;
padding: 6px 14px;
border-radius: 4px;
font-weight: 600;
color: #080707;
text-decoration: none;
transition: background-color 0.2s ease;
}
.ap-tree-node-badge:hover {
background-color: #eef4ff;
border-color: #0176d3;
color: #0176d3;
}
</style>
<apex:sectionHeader title="Account Hierarchy" subtitle="Visual Tree View" />
<apex:form>
<apex:pageBlock title="Parent-Child Organizations">
<div class="ap-tree-container">
<ul class="ap-tree">
<apex:repeat value="{!treeNodes}" var="root">
<li>
<a href="/{!root.id}" target="_blank" class="ap-tree-node-badge">
๐ {!root.name}
</a>
<apex:outputPanel rendered="{!root.children.size > 0}">
<ul>
<apex:repeat value="{!root.children}" var="child">
<li>
<a href="/{!child.id}" target="_blank" class="ap-tree-node-badge">
๐ {!child.name}
</a>
</li>
</apex:repeat>
</ul>
</apex:outputPanel>
</li>
</apex:repeat>
</ul>
</div>
</apex:pageBlock>
</apex:form>
</apex:page>
3. Modern Alternative: Native LWC Tree Grid
In modern Salesforce Lightning development, building nested unordered lists in Visualforce is replaced by the standard <lightning-tree-grid> component in Lightning Web Components (LWC):
- Built-in Collapse/Expand: Handles multi-level nesting, row selection, sorting, and inline expansion natively without writing custom DOM manipulation code.
- Zero Custom CSS Needed: Fully compliant with Salesforce Lightning Design System (SLDS) styling and accessibility standards.
- Asynchronous Child Loading: Easily fetches nested branches dynamically when a user clicks expand, saving client-side memory.
<!-- Modern LWC Tree Grid Example (accountTreeGrid.html) -->
<template>
<lightning-card title="Enterprise Account Hierarchy" icon-name="standard:hierarchy">
<div class="slds-p-around_medium">
<lightning-tree-grid
columns={gridColumns}
data={gridData}
key-field="id"
hide-checkbox-column>
</lightning-tree-grid>
</div>
</lightning-card>
</template>
4. Common Traps & Performance Best Practices
Writing recursive SOQL queries inside helper loops to fetch deep hierarchy tiers quickly hits the 100 SOQL query limit. Always use parent-to-child relationship subqueries (e.g.,
SELECT Id, (SELECT Id FROM ChildAccounts) FROM Account) or query all matching records in a single batch and build the hierarchy in-memory using an Apex Map (Map<Id, List<Account>>).
<lightning-tree-grid> for the highest performance.
- Handle Circular Hierarchies: Implement recursion depth checks or track visited record IDs in a
Set<Id>to prevent infinite loops if an account is mistakenly set as its own grandparent. - Include Direct Record Hyperlinks: Always provide clickable URLs on tree node labels so users can navigate directly to the target Salesforce record page.
- Enable lightningStylesheets: Always include
lightningStylesheets="true"on the Visualforce page tag to match Lightning Experience styling.
Summary
Hierarchical tree views transform complex relational datasets into clear, interactive parent-child diagrams. By pairing recursive Apex wrapper models with styled Visualforce templates or upgrading to modern <lightning-tree-grid> LWC components, developers can provide seamless record exploration across enterprise account and product trees.