PrimeNG TreeTable: Lazy Loading, Pagination, and Error‑Safe Setup
Implement a PrimeNG TreeTable with efficient lazy loading, integrated pagination, and robust error handling. Follow this concise guide for data binding, unique key enforcement, and verification steps.
29 Mar 2026, 14:06 UTC

Problem: Displaying Hierarchical Data Efficiently
When an Angular application needs to show thousands of records in a tree structure, rendering the entire dataset up front is a performance nightmare. PrimeNG’s p-treeTable component offers lazy loading to fetch child nodes on demand, but developers often stumble on data shape, key uniqueness, and pagination integration. The goal is to set up a TreeTable that loads nodes only when expanded, paginates the top‑level rows, and handles errors gracefully.
Prerequisites
- Angular 17+ (or the version you are using)
- PrimeNG 14.0.0 or newer – earlier releases lack the
onNodeExpandAPI. - PrimeNG CSS theme (e.g.,
theme.css) and core styles. - HTTP client for server communication.
- Unique identifiers for every node (the
keyproperty).
Data Model
TreeTable expects an array of node objects. Each node must contain:
key– a unique string or number.label– the text displayed in the tree column.data– an optional object with additional columns.children– an array of child nodes (may be empty).expanded– optional boolean to pre‑expand a node.leaf– optional boolean to indicate no children.
[
{
"key": "1",
"label": "Root 1",
"data": { "size": "15 MB", "type": "folder" },
"children": [],
"expanded": false,
"leaf": false
}
]
Module Setup
Import the required PrimeNG modules in your feature module:
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TreeTableModule } from 'primeng/treetable';
import { PaginatorModule } from 'primeng/paginator';
import { HttpClientModule } from '@angular/common/http';
@NgModule({
imports: [
CommonModule,
HttpClientModule,
TreeTableModule,
PaginatorModule
],
declarations: [MyTreeTableComponent]
})
export class MyFeatureModule { }
Component Template
The template wires the TreeTable to lazy loading and wraps it in a paginator. The lazy attribute tells the component to wait for the onNodeExpand event before rendering children.
<div>
<p-paginator [rows]="rows" [totalRecords]="totalRecords" (onPageChange)="onPageChange($event)"
[showFirstLastButtons]="true"></p-paginator>
<p-treeTable [value]="nodes" [lazy]="true" (onNodeExpand)="onNodeExpand($event)"
[columns]="cols">
<ng-template pTemplate="header" let-columns>
<tr>
<th *ngFor="let col of columns">{{col.header}}</th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-rowNode let-columns="columns">
<tr>
<td>{{rowNode.data.size}}</td>
<td>{{rowNode.data.type}}</td>
</tr>
</ng-template>
</p-treeTable>
</div>
Component Class
The class handles lazy loading, pagination, and error state. Note the use of BehaviorSubject to trigger updates when new data arrives.
import { Component, OnInit } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { TreeNode } from 'primeng/api';
import { BehaviorSubject } from 'rxjs';
@Component({
selector: 'app-my-tree-table',
templateUrl: './my-tree-table.component.html'
})
export class MyTreeTableComponent implements OnInit {
nodes: TreeNode[] = [];
cols = [
{ field: 'size', header: 'Size' },
{ field: 'type', header: 'Type' }
];
rows = 10; // rows per page
totalRecords = 0;
private page$ = new BehaviorSubject(0);
constructor(private http: HttpClient) {}
ngOnInit() {
this.page$.subscribe(page => this.loadRoot(page));
}
loadRoot(page: number) {
// Query parameters: page, size
this.http.get<TreeNode[]>(`/api/nodes?page=${page}&size=${this.rows}`)
.subscribe(data => {
this.nodes = data;
this.totalRecords = 100; // set from server if available
}, err => {
console.error('Failed to load root nodes', err);
});
}
onNodeExpand(event: any) {
const node: TreeNode = event.node;
// Avoid duplicate requests
if (node.children && node.children.length) return;
this.http.get<TreeNode[]>(`/api/nodes/${node.key}/children`)
.subscribe(children => {
node.children = children;
node.expanded = true;
}, err => {
console.error('Lazy load error for node', node.key, err);
});
}
onPageChange(event: any) {
this.rows = event.rows;
this.page$.next(event.page);
}
}
Server API Contract
When the client requests children for a node, the server must return an array of fully populated node objects, including key and label. Returning a partial structure (missing children or key) will break lazy loading and UI rendering.
[
{ "key": "1-1", "label": "Child 1", "data": { "size": "1 MB", "type": "file" }, "leaf": true }
]
Styling
- Base class:
.p-treetable - Row class:
.p-treetable-row - Custom theme overrides can be added by targeting
.p-treetable .p-treetable-bodyor similar selectors.
Verification Checklist
- Render Test – Inspect the DOM for
.p-treetableand verify each row has.p-treetable-row. - Lazy Load Test – Expand a node and watch the network panel for a GET request to
/api/nodes/{key}/children. The response should contain achildrenarray. - Pagination Test – Change the paginator to the second page and confirm the TreeTable updates without reloading the entire component.
- Error Handling – Simulate a 500 response from the server and confirm the console logs an error without breaking the UI.
Error Handling
Common pitfalls include:
- Duplicate
keyvalues – use UUIDs or database IDs. - Missing
childrenproperty – the component will treat the node as a leaf. - Server returning
null– guard against by checkingArray.isArray(children)before assigning.
Limitations & Recommendations
- PrimeNG’s TreeTable does not support server‑side pagination out of the box; wrapping the component in
p-paginatoris a workaround that only paginates the root nodes. - Lazy loading requires the server to return the full node structure; partial data leads to broken UI.
- Always keep
totalRecordsin sync with the server to display correct paginator controls.
Conclusion
By following the data model, module imports, template wiring, and server contract outlined above, you can deploy a PrimeNG TreeTable that loads large hierarchies efficiently, supports pagination, and remains robust against common errors. Verify each step with the checklist, and you’ll have a production‑ready component that scales with your data.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.