Infinite Scroll (Load More)
Problem
The full dataset is too large to load at once. As the user scrolls near the bottom, the next page should be fetched and appended — extending the list without remounting it.
Solution
Detect when the user scrolls near the end and load the next page. Update count through update() to append new items seamlessly.
ts
import { createVirtualizer } from '@vielzeug/virtualit';
const PAGE_SIZE = 50;
let items: string[] = Array.from({ length: PAGE_SIZE }, (_, i) => `Item ${i}`);
let loading = false;
async function loadMore() {
if (loading) return;
loading = true;
await new Promise((r) => setTimeout(r, 500)); // simulate API call
const start = items.length;
items = [...items, ...Array.from({ length: PAGE_SIZE }, (_, i) => `Item ${start + i}`)];
virt.update({ count: items.length });
loading = false;
}
const virt = createVirtualizer(scrollEl, {
count: items.length,
estimateSize: 40,
onChange: (virtualItems, totalSize) => {
listEl.style.height = `${totalSize}px`;
listEl.innerHTML = '';
for (const item of virtualItems) {
const el = document.createElement('div');
el.style.cssText = `position:absolute;top:${item.start}px;left:0;right:0;height:40px;line-height:40px;padding:0 12px;`;
el.textContent = items[item.index] ?? 'Loading…';
listEl.appendChild(el);
}
// Trigger next page load when the last item is in view
const lastRendered = virtualItems.at(-1);
if (lastRendered && lastRendered.index >= items.length - 10) {
loadMore();
}
},
});Pitfalls
- Off-by-one in the scroll-end check (
>vs>=) causes a double-trigger or a missed trigger at the boundary. Verify the comparison againstcount - threshold, notcount - 1. - Not gating the load with an
isLoadingflag triggers duplicate fetches when the user is near the bottom while a request is in flight. - After loading the last page,
update({ count })with the same count as before does not trigger a re-render. Handle the "all pages loaded" state explicitly and stop observing scroll.