scroll-pagination-engine
v1.0.1
Published
A lightweight, framework-neutral scroll pagination core with a React adapter.
Maintainers
Readme
Scroll Pagination
Framework-neutral infinite scroll pagination for modern web applications.
The package provides a small browser-based core that works with plain JavaScript and any UI framework. A dedicated React hook is included for React and Next.js applications.
Why Scroll Pagination?
- Framework neutral: Use the same pagination logic with Vue, Svelte, Solid, Angular, Preact, React, Next.js, or plain DOM code.
- Small API: Start, stop, load, and reset pagination without a large state-management layer.
- Async-first: Works naturally with
fetch, API clients, and any Promise-based data source. - Duplicate-load protection: Prevents concurrent requests while a page is loading.
- End-of-list handling: Return
falsefromonLoadMoreto stop requesting pages. - SSR friendly: The core does not access browser globals until
start()is called. - TypeScript support: Public options and callback types are included in the package declarations.
- React adapter: Use
useScrollPaginationwith IntersectionObserver in React and Next.js.
Requirements
- Node.js 18 or newer for installation and development.
- A browser with
window,document, and scroll events for the core runtime. - React 18 or newer only when using the React adapter.
Installation
npm install scroll-pagination-engineThe core package has no required framework dependency. React and React DOM are optional peer dependencies and are needed only for the React adapter.
How to Use
Core API
Import ScrollPagination from the package root. Create it when the page or component is initialized, call start() after it mounts, and call stop() when it is destroyed.
import { ScrollPagination } from "scroll-pagination-engine";
type Post = {
id: number;
title: string;
};
const posts: Post[] = [];
const pagination = new ScrollPagination({
threshold: 300,
initialPage: 1,
onLoadingChange(loading) {
showLoadingIndicator(loading);
},
onError(error) {
showError(error.message);
},
async onLoadMore(page) {
const response = await fetch(`/api/posts?page=${page}`);
if (!response.ok) {
throw new Error("Unable to load posts.");
}
const data: Post[] = await response.json();
posts.push(...data);
renderPosts(posts);
// Return false when the API has no more records.
return data.length > 0;
},
});
pagination.start();
// When the page or view is destroyed:
pagination.stop();You can also trigger a request manually and inspect or reset the state:
await pagination.loadMore();
console.log(pagination.getState());
// { page: 2, loading: false, hasMore: true }
pagination.reset();React and Next.js
Import the hook from the React adapter subpath:
import { useScrollPagination } from "scroll-pagination-engine/react";
export function PostList() {
const {
page,
loading,
error,
hasMore,
observerRef,
reset,
} = useScrollPagination({
async onLoadMore(page) {
const response = await fetch(`/api/posts?page=${page}`);
const data = await response.json();
appendPosts(data);
return data.length > 0;
},
});
return (
<>
<p>Current page: {page}</p>
<PostItems />
{loading && <p>Loading...</p>}
{error && <p>{error.message}</p>}
{hasMore && <div ref={observerRef} style={{ height: 1 }} />}
<button type="button" onClick={reset}>
Reset
</button>
</>
);
}For Next.js App Router, add "use client" at the top of the component file because the hook uses browser APIs and React state.
Other Frameworks
The core API can be used in any framework with a component lifecycle:
| Framework | Mount hook | Cleanup hook |
|-----------|------------|--------------|
| Vue 3 | onMounted(() => pagination.start()) | onUnmounted(() => pagination.stop()) |
| Svelte | onMount(() => { ... }) | Return a cleanup function from onMount |
| Solid | onMount(() => pagination.start()) | onCleanup(() => pagination.stop()) |
| Angular | ngOnInit() | ngOnDestroy() |
| Preact | useEffect() | Return cleanup from useEffect |
| Plain DOM | After DOM initialization | Before removing the view |
Complete examples are available in examples/frameworks/README.md.
API Reference
PaginationOptions
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| onLoadMore | (page: number) => Promise<boolean \| void> | Required | Loads one page. Return false when there are no more pages. |
| threshold | number | 300 | Distance in pixels from the bottom before loading. |
| initialPage | number | 1 | Page number used for the first request. |
| onLoadingChange | (loading: boolean) => void | Optional | Called when loading starts or ends. |
| onPageChange | (page: number) => void | Optional | Called after the next page number is prepared. |
| onError | (error: Error) => void | Optional | Called when onLoadMore throws. |
ScrollPagination Methods
| Method | Description |
|--------|-------------|
| start() | Registers the window scroll listener. Safe to call only in a browser lifecycle hook. |
| stop() | Removes the window scroll listener. |
| loadMore() | Loads the current page manually. Concurrent calls are ignored. |
| reset() | Restores initialPage, loading state, and the end-of-list state. |
| getState() | Returns page, loading, and hasMore. |
React Hook Options
The React adapter additionally supports enabled, root, rootMargin, threshold, immediate, onSuccess, and reset/loadMore controls. It observes the returned observerRef with IntersectionObserver instead of listening to the window scroll event.
Examples
Development
npm install
npm run typecheck
npm test
npm run buildLicense
MIT
