Infinite Scroll
Load more rows on demand with current ZenGrid infinite scrolling options.
ZenGrid infinite scrolling loads additional row batches when the user scrolls near the bottom of the loaded data. It is useful when you can append data incrementally and do not need a known total row count up front.
Basic Setup
import { Grid, type ColumnDef } from '@zengrid/core';import '@zengrid/core/dist/styles.css';
const columns: ColumnDef[] = [ { field: 'id', header: 'ID', width: 80 }, { field: 'name', header: 'Name', width: 180 }, { field: 'email', header: 'Email', width: 240 },];
async function loadUsers(offset: number, limit: number): Promise<any[][]> { const response = await fetch(`/api/users?offset=${offset}&limit=${limit}`); if (!response.ok) throw new Error('Failed to load users');
const result = await response.json(); return result.users.map((user: any) => [user.id, user.name, user.email]);}
const firstPage = await loadUsers(0, 100);
const grid = new Grid(container, { rowCount: firstPage.length, colCount: columns.length, rowHeight: 36, colWidth: columns.map((column) => column.width ?? 140), columns, infiniteScrolling: { enabled: true, threshold: 20, initialRowCount: firstPage.length, }, onLoadMoreRows: async (currentRowCount) => { return loadUsers(currentRowCount, 100); },});
grid.setData(firstPage);grid.render();For known-total datasets with server-side sorting, filtering, and paging, backend dataMode plus onDataRequest is usually easier to reason about.
Callback Signature
type LoadMoreRowsCallback = (currentRowCount: number) => Promise<any[][]>;The callback receives the current loaded row count. Return row arrays in the same column order as the grid.
Sliding Window
Use the sliding window options when loaded data can grow large enough to make memory usage matter.
const grid = new Grid(container, { rowCount: firstPage.length, colCount: columns.length, rowHeight: 36, colWidth: columns.map((column) => column.width ?? 140), columns, infiniteScrolling: { enabled: true, threshold: 30, initialRowCount: firstPage.length, enableSlidingWindow: true, windowSize: 1000, pruneThreshold: 1200, onDataPruned: (prunedRowCount, newVirtualOffset) => { console.log({ prunedRowCount, newVirtualOffset }); }, }, onLoadMoreRows: (currentRowCount) => loadUsers(currentRowCount, 100),});pruneThreshold is a row-count threshold. When the in-memory window grows past it,
older rows can be pruned according to the sliding-window strategy.
Stats And Reset
const stats = grid.getSlidingWindowStats();console.log(stats.rowsInMemory, stats.totalRowsLoaded, stats.prunedRows);
grid.resetInfiniteScrolling();Use resetInfiniteScrolling() after changing a search query or filter that affects
the append stream.
Search Example
let query = '';
async function loadSearchPage(offset: number): Promise<any[][]> { const response = await fetch('/api/search', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query, offset, limit: 100 }), });
const result = await response.json(); return result.rows;}
document.querySelector<HTMLInputElement>('#search')?.addEventListener('input', async (event) => { query = event.currentTarget.value;
const firstPage = await loadSearchPage(0); grid.setData(firstPage); grid.resetInfiniteScrolling();});Production Notes
- Handle network errors in
onLoadMoreRows; returning an empty array is safer than throwing into the UI. - Debounce search inputs before resetting the stream.
- Cache recently fetched pages server-side if users often scroll back through the same data.
- Tune
threshold,windowSize, andpruneThresholdwith real row renderers, not synthetic rows only.