TanStack Query Infinite Cursor Pagination
Drive cursor-based infinite lists with TanStack Query useInfiniteQuery, threading nextCursor through pages and prefetching ahead of scroll.
Code
import {
useInfiniteQuery, queryClient, keepPreviousData,
} from '@tanstack/react-query';
interface FeedPage {
items: { id: string; title: string }[];
nextCursor: string | null;
}
async function fetchFeed(cursor?: string): Promise<FeedPage> {
const params = new URLSearchParams({ limit: '20' });
if (cursor) params.set('cursor', cursor);
const res = await fetch('/api/feed?' + params.toString());
if (!res.ok) throw new Error('feed failed: ' + res.status);
return res.json();
}
function useFeed() {
return useInfiniteQuery({
queryKey: ['feed'],
queryFn: ({ pageParam }) => fetchFeed(pageParam),
initialPageParam: undefined as string | undefined,
getNextPageParam: (lastPage) => lastPage.nextCursor ?? undefined,
staleTime: 30_000,
placeholderData: keepPreviousData,
});
}
function FeedList() {
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useFeed();
const items = data?.pages.flatMap((page) => page.items) ?? [];
return (
<section>
{items.map((item) => <article key={item.id}>{item.title}</article>)}
<button
onClick={() => fetchNextPage()}
disabled={!hasNextPage || isFetchingNextPage}
>
{isFetchingNextPage ? 'loading…' : 'load more'}
</button>
</section>
);
}
queryClient.prefetchInfiniteQuery({
queryKey: ['feed'],
queryFn: ({ pageParam }) => fetchFeed(pageParam),
initialPageParam: undefined as string | undefined,
pages: 1,
});Report a problem with this exampleOpens a pre-filled issue on GitHub.