Skip to main content

Infinite Scroll

High-performance infinite scroll hook for React using the Intersection Observer API.

Installation

npm install @windowing-kit/infinite-scroll
# or
pnpm add @windowing-kit/infinite-scroll
# or
yarn add @windowing-kit/infinite-scroll

Quick Start

import { useInfiniteScroll } from "@windowing-kit/infinite-scroll";
import { useInfiniteState } from "./useInfiniteState";

function MyInfiniteList() {
const { items, hasNextPage, loading, fetchData } = useInfiniteState();

const { ref, isFetching } = useInfiniteScroll({
loading,
hasNextPage,
onLoadMore: fetchData,
rootMargin: "200px",
initialLoad: true,
});

return (
<div>
{items.map((item) => (
<div key={item.id}>{item.name}</div>
))}
<div ref={ref}>
{isFetching && <div>Loading...</div>}
{!hasNextPage && <div>No more items</div>}
</div>
</div>
);
}

API

useInfiniteScroll(options)

Infinite scroll hook.

Options

PropertyTypeRequiredDefaultDescription
loadingbooleanYes-Whether data is currently being loaded
hasNextPagebooleanYes-Whether there is more data to load
onLoadMore() => void | Promise<void>Yes-Callback function to load more data
rootMarginstringNo"0px"Root margin for Intersection Observer
disabledbooleanNofalseWhether to disable infinite scroll
delayInMsnumberNo100Delay in milliseconds before triggering onLoadMore
initialLoadbooleanNofalseWhether to automatically load data on initial mount

Return

PropertyTypeDescription
refVisibilityRefCallbackRef callback to attach to the trigger element (usually the last item or sentinel)
isFetchingbooleanWhether data is currently being fetched (same as loading)

Examples

Basic Usage

function InfiniteList() {
const { items, hasNextPage, loading, fetchData } = useInfiniteState();

const { ref, isFetching } = useInfiniteScroll({
loading,
hasNextPage,
onLoadMore: fetchData,
});

return (
<div>
{items.map((item) => (
<Item key={item.id} item={item} />
))}
<div ref={ref}>{isFetching && <Spinner />}</div>
</div>
);
}

With Custom Root Margin

const { ref, isFetching } = useInfiniteScroll({
loading,
hasNextPage,
onLoadMore: loadMore,
rootMargin: "300px", // Start loading 300px before reaching the bottom
});

Disable on Condition

const { ref, isFetching } = useInfiniteScroll({
loading,
hasNextPage,
onLoadMore: loadMore,
disabled: !user.isAuthenticated, // Disable if user is not authenticated
});

Initial Load

const { ref, isFetching } = useInfiniteScroll({
loading,
hasNextPage,
onLoadMore: loadMore,
initialLoad: true, // Automatically load data when component mounts
});