Infinite Scroll

A scroll container that automatically loads more content when the user approaches the bottom. Uses an IntersectionObserver sentinel, so it works in any scrollable area with no manual scroll listeners.

Usage

<script>
  import { InfiniteScroll } from '@vultra/ui';

  let items = $state([...Array(25).keys()]);
  let loading = $state(false);
  let hasMore = $state(true);

  async function loadMore() {
    loading = true;
    await new Promise((r) => setTimeout(r, 600));
    const next = items.length + 1;
    items = [...items, ...Array.from({ length: 25 }, (_, i) => next + i)];
    hasMore = items.length < 250;
    loading = false;
  }
</script>

<InfiniteScroll
  bind:hasMore
  bind:loading
  {onLoadMore: loadMore}
  class="h-96 overflow-y-auto"
>
  {#each items as item}
    <div class="border-b p-4">Item {item}</div>
  {/each}
</InfiniteScroll>

Props

PropTypeDefaultDescription
hasMorebooleantrueWhether more items exist (bindable)
loadingbooleanfalseLoading state; shows a spinner when true (bindable)
errorbooleanfalseError state; shows a retry button when true (bindable)
onLoadMore() => void \| Promise<void>Called when the sentinel scrolls into view (required)
onRetry() => void \| Promise<void>Called when the retry button is pressed
thresholdnumber200Distance (px) from the bottom that triggers a load
classstringAdditional classes (add overflow for the scroll container)

States

  • Loading — a spinner replaces the bottom sentinel while loading is true
  • Error — a message with a Retry button (falls back to onLoadMore if onRetry isn’t set)
  • End — a “No more items” message appears when hasMore becomes false

Features

  • IntersectionObserver-based auto-loading — no scroll-throttling
  • Built-in loading, error, and end states
  • Bindable loading/error/hasMore so you control data flow

Install

npx @vultra/cli add infinite-scroll