Skip to content

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/scroll';

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: ({ items, totalSize }) => {
    listEl.style.height = `${totalSize}px`;
    listEl.replaceChildren();

    for (const item of items) {
      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 = items.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 against count - threshold, not count - 1.
  • Not gating the load with an isLoading flag 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.