PrimeNG p‑Table Virtual Scrolling: Quick Setup, Key Options, and Common Pitfalls
PrimeNG’s p‑Table virtual scrolling lets you render only the rows visible in the viewport, cutting DOM size and boosting performance. This guide shows how to enable it, configure lazy loading, and avoid common pitfalls like mixing pagination or overriding row height.
04 Jul 2025, 11:33 UTC

Why Virtual Scrolling?
When a table holds thousands of rows, rendering every <tr> in the DOM can cripple performance. PrimeNG’s p-table virtual scroll solves this by keeping only the rows that are actually visible in the viewport. This reduces the DOM size from n rows to roughly rows + a small buffer, giving a fast, smooth scroll even with huge datasets.
Getting Started: Minimal Working Example
Below is a minimal Angular component that demonstrates virtual scrolling with lazy loading. Replace fetchData with your own API call.
// component.ts
import { Component } from '@angular/core';
import { LazyLoadEvent } from 'primeng/api';
@Component({
selector: 'app-virtual-table',
templateUrl: './virtual-table.component.html'
})
export class VirtualTableComponent {
data: any[] = [];
totalRecords = 0;
// Called by PrimeNG when the user scrolls
loadData(event: LazyLoadEvent) {
const page = event.first! / event.rows!;
fetchData(page, event.rows!).then(res => {
this.data = res.items;
this.totalRecords = res.total;
});
}
}
// fake API for illustration
function fetchData(page: number, size: number) {
// Simulate 10,000 items
const total = 10000;
const start = page * size;
const end = Math.min(start + size, total);
const items = Array.from({ length: end - start }, (_, i) => ({
id: start + i,
name: `Item ${start + i}`
}));
return Promise.resolve({ items, total });
}
ID
Name
{{row.id}}
{{row.name}}
The key attributes are:
[virtualScroll]="true"– enables the virtual buffer.[scrollHeight]="'400px'"– gives the table a fixed height so the scrollbar appears.[rows]="30"– tells PrimeNG how many rows fit in the viewport. This value must match the CSS row height.[lazy]="true"+(onLazyLoad)– loads data on demand as the user scrolls.[totalRecords]– required for the lazy loader to know the full dataset size.
Matching Row Height with CSS
PrimeNG calculates the number of rows that fit in the viewport based on the [rows] value. If you override the row height via CSS (for example, using padding or line-height), the component will mis‑align the buffer, causing rows to appear duplicated or missing. Always keep the CSS height consistent with [rows] or update [rows] accordingly.
Common Pitfalls & How to Avoid Them
1. Mixing Pagination and Virtual Scroll
PrimeNG’s p-table supports either pagination or virtual scrolling, not both. If you set [paginator]="true" while [virtualScroll]="true", the paginator will be ignored and the table will behave as if virtual scroll is the sole navigation method.
2. Omitting [scrollHeight]
Without a fixed height the table will expand to fit all rows, so the scrollbar never appears and virtual scroll is effectively disabled. Always provide [scrollHeight] (or [scrollWidth] for horizontal scrolling).
3. Enabling Unsupported Features
Features that alter row rendering—such as row grouping, column resizing, or row selection with checkboxes—break the virtual scroll logic. Disable them when you need virtual scrolling.
4. Returning Fewer Rows Than Requested
The lazy load callback must return exactly event.rows items (or the remaining items if at the end). If you return fewer, PrimeNG will render blank rows. Validate the slice size before assigning to data.
5. CSS Overrides of Row Height
Changing tr or td padding/margin without updating [rows] can cause misalignment. If you need custom styling, adjust [rows] to match the new computed height.
Performance Gains
With virtual scroll, a table that could render 10,000 rows will only have around 30–50 rows in the DOM at any time. This dramatically lowers memory usage and improves scroll latency, especially on mobile or low‑power devices.
Verification Checklist
- Run the component and confirm the scrollbar appears at 400 px height.
- Open the browser’s DevTools and inspect the table body – you should see only
rows+ buffer<tr>elements. - Scroll to the bottom and verify new rows appear without a full page reload.
- Check the network panel to see only the lazy load requests, not a full dataset fetch.
When to Use Virtual Scrolling
- Large datasets where loading the entire list into memory is impractical.
- Environments with limited client resources (mobile, embedded).
- When you need a smooth scrolling experience without pagination flicker.
Conclusion
PrimeNG’s virtual scrolling is a powerful tool for handling massive tables efficiently. By correctly setting [virtualScroll], [scrollHeight], [rows], and [lazy], and by avoiding conflicting features, you can deliver a responsive UI that scales gracefully.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.