Load data on demand with fetch
For data sets that live on a server, size the data array to the total number of records up front so the scroll bar is right, fill it with placeholder rows, and load a page whenever the user scrolls near rows that have not been fetched. scrollIndexRect tells you which rows are in view.
import canvasDatagrid from 'canvas-datagrid'; const app = document.getElementById('app'); const gridElement = document.createElement('div'); gridElement.style.height = '400px'; const total = 200; // the API has 200 records const pageSize = 20; const loading = 'Loading...'; const data = Array.from({ length: total }, (_, id) => ({ id: id + 1, title: loading, completed: '', loaded: false, })); const pending = new Set<number>(); const grid = canvasDatagrid({ parentNode: gridElement, data, editable: false, schema: [ { name: 'id', width: 60 }, { name: 'title', width: 420 }, { name: 'completed', width: 100 }, ], }); grid.style.height = '100%'; grid.style.width = '100%'; async function loadPage(page: number) { if (pending.has(page)) return; pending.add(page); const start = page * pageSize; const response = await fetch( `https://jsonplaceholder.typicode.com/todos?_start=${start}&_limit=${pageSize}`, ); const rows = await response.json(); rows.forEach((row: any, index: number) => { const target = data[start + index]; target.title = row.title; target.completed = row.completed ? 'yes' : 'no'; target.loaded = true; }); grid.draw(); } function loadVisiblePages() { const { top, bottom } = grid.scrollIndexRect; for (let row = top; row <= bottom && row < total; row++) { if (!data[row].loaded) loadPage(Math.floor(row / pageSize)); } } let debounce: number | undefined; grid.addEventListener('scroll', function () { window.clearTimeout(debounce); debounce = window.setTimeout(loadVisiblePages, 100); }); app.append(gridElement); loadVisiblePages();