Skip to content

Infinite scroll

Problem. Append new items as the user reaches the bottom of a list.

Solution. useInfiniteFetch combined with useElementVisibility from Media.

ts
import { useInfiniteFetch } from '@bquery/bquery/reactive';
import { useElementVisibility } from '@bquery/bquery/media';
import { effect } from '@bquery/bquery/reactive';

const feed = useInfiniteFetch<Post[], Post[]>((cursor) => `/api/posts?cursor=${cursor ?? ''}`, {
  getNextCursor: (page) => (page.length > 0 ? page[page.length - 1].id : undefined),
  transform: (pages) => pages.flat(),
});

const sentinel = document.querySelector('#sentinel') as HTMLElement;
const visible = useElementVisibility(sentinel);

effect(() => {
  if (visible.value && !feed.pending.value && feed.hasMore.value) {
    void feed.fetchNextPage();
  }

  const items = feed.data.value;
  if (items) renderFeed(items);
});

Why it works. The IntersectionObserver-backed useElementVisibility keeps observation cheap; hasMore short-circuits requests when the API reports the last page.

Released under the MIT License.