useInfiniteList is a modified version of TanStack Query’s useInfiniteQuery for retrieving items from a resource with infinite scrolling/pagination support.
It uses the getList method from the dataProvider.
Usage
import { useInfiniteList } from "@refinedev/core" ;
interface IProduct {
id : number ;
name : string ;
price : number ;
}
const productList = useInfiniteList < IProduct >({
resource: "products" ,
pagination: {
current: 1 ,
pageSize: 10 ,
},
});
const { data , hasNextPage , fetchNextPage } = productList . query ;
Parameters
Resource name for API data interactions.
Pagination configuration for the query. Initial number of items per page
mode
'client' | 'server' | 'off'
default: "'server'"
Whether to use server side pagination or not
Sorter parameters for ordering results.
Filter parameters for narrowing down results. Show LogicalFilter properties
Filter operator: eq, ne, lt, gt, lte, gte, in, nin, contains, etc.
Meta data for the dataProvider. Can be used to pass additional parameters to data provider methods.
If there is more than one dataProvider, you should specify which one to use.
TanStack Query’s useInfiniteQuery options. Time in milliseconds after data is considered stale
Initial page parameter value
getNextPageParam
(lastPage) => number | undefined
Custom function to determine the next page parameter. By default, uses built-in pagination logic.
successNotification
OpenNotificationParams | false | ((data, params) => OpenNotificationParams | false)
Success notification configuration. Set to false to disable.
errorNotification
OpenNotificationParams | false | ((error, params) => OpenNotificationParams | false)
Error notification configuration. Set to false to disable.
liveMode
'auto' | 'manual' | 'off'
Live/Realtime mode configuration.
Callback to handle live events.
Additional parameters for live queries.
onSuccess
(data: InfiniteData<GetListResponse<TData>>) => void
Callback function called on successful query.
Callback function called on failed query.
overtimeOptions
UseLoadingOvertimeOptionsProps
Configuration for loading overtime behavior.
Return Values
Simplified result object. data
InfiniteData<GetListResponse<TData>> | undefined
All pages of data
Whether there is a next page available
Whether there is a previous page available
TanStack Query’s useInfiniteQuery return object. data
InfiniteData<GetListResponse<TData>>
All pages of data with pagination info
Loading state (true on initial load)
Fetching state (true on any fetch including refetch)
Whether the next page is being fetched
Whether the previous page is being fetched
Error object if the query failed
Whether there are more pages to fetch
Whether there are previous pages
fetchNextPage
() => Promise<InfiniteQueryObserverResult>
Function to fetch the next page
fetchPreviousPage
() => Promise<InfiniteQueryObserverResult>
Function to fetch the previous page
refetch
() => Promise<InfiniteQueryObserverResult>
Function to refetch all pages
Loading overtime information. Elapsed time in milliseconds
Examples
import { useInfiniteList } from "@refinedev/core" ;
const { query } = useInfiniteList ({
resource: "posts" ,
pagination: {
pageSize: 10 ,
},
});
const { data , fetchNextPage , hasNextPage , isFetchingNextPage } = query ;
// Flatten all pages into a single array
const allPosts = data ?. pages . flatMap (( page ) => page . data ) ?? [];
return (
< div >
{ allPosts . map (( post ) => (
< div key = { post . id } > { post . title } </ div >
)) }
{ hasNextPage && (
< button
onClick = { () => fetchNextPage () }
disabled = { isFetchingNextPage }
>
{ isFetchingNextPage ? "Loading..." : "Load More" }
</ button >
) }
</ div >
);
import { useInfiniteList } from "@refinedev/core" ;
import { useEffect , useRef } from "react" ;
const ProductList = () => {
const { query } = useInfiniteList ({
resource: "products" ,
pagination: { pageSize: 20 },
});
const { data , fetchNextPage , hasNextPage , isFetchingNextPage } = query ;
const loadMoreRef = useRef < HTMLDivElement >( null );
useEffect (() => {
if ( ! hasNextPage || isFetchingNextPage ) return ;
const observer = new IntersectionObserver (
( entries ) => {
if ( entries [ 0 ]. isIntersecting ) {
fetchNextPage ();
}
},
{ threshold: 1 }
);
const currentRef = loadMoreRef . current ;
if ( currentRef ) {
observer . observe ( currentRef );
}
return () => {
if ( currentRef ) {
observer . unobserve ( currentRef );
}
};
}, [ hasNextPage , isFetchingNextPage , fetchNextPage ]);
const allProducts = data ?. pages . flatMap (( page ) => page . data ) ?? [];
return (
< div >
{ allProducts . map (( product ) => (
< div key = { product . id } > { product . name } </ div >
)) }
< div ref = { loadMoreRef } >
{ isFetchingNextPage && < div > Loading more... </ div > }
</ div >
</ div >
);
};
With Filters and Sorting
const { query } = useInfiniteList ({
resource: "posts" ,
pagination: { pageSize: 10 },
filters: [
{
field: "status" ,
operator: "eq" ,
value: "published" ,
},
],
sorters: [
{
field: "createdAt" ,
order: "desc" ,
},
],
});
Accessing Page Information
const { query } = useInfiniteList ({
resource: "posts" ,
pagination: { pageSize: 10 },
});
const { data } = query ;
// Access individual pages
data ?. pages . forEach (( page , index ) => {
console . log ( `Page ${ index + 1 } :` , page . data );
console . log ( `Total:` , page . total );
console . log ( `Pagination:` , page . pagination );
});
// Get total number of items across all pages
const totalItems = data ?. pages [ 0 ]?. total ;
// Get all items flattened
const allItems = data ?. pages . flatMap (( page ) => page . data ) ?? [];
import { useInfiniteList } from "@refinedev/core" ;
import { useVirtualizer } from "@tanstack/react-virtual" ;
import { useRef } from "react" ;
const VirtualList = () => {
const { query } = useInfiniteList ({
resource: "posts" ,
pagination: { pageSize: 50 },
});
const { data , fetchNextPage , hasNextPage } = query ;
const allRows = data ?. pages . flatMap (( page ) => page . data ) ?? [];
const parentRef = useRef < HTMLDivElement >( null );
const rowVirtualizer = useVirtualizer ({
count: hasNextPage ? allRows . length + 1 : allRows . length ,
getScrollElement : () => parentRef . current ,
estimateSize : () => 50 ,
overscan: 5 ,
});
useEffect (() => {
const [ lastItem ] = [ ... rowVirtualizer . getVirtualItems ()]. reverse ();
if ( ! lastItem ) return ;
if (
lastItem . index >= allRows . length - 1 &&
hasNextPage &&
! query . isFetchingNextPage
) {
fetchNextPage ();
}
}, [
hasNextPage ,
fetchNextPage ,
allRows . length ,
query . isFetchingNextPage ,
rowVirtualizer . getVirtualItems (),
]);
return (
< div ref = { parentRef } style = { { height: "600px" , overflow: "auto" } } >
< div
style = { {
height: ` ${ rowVirtualizer . getTotalSize () } px` ,
position: "relative" ,
} }
>
{ rowVirtualizer . getVirtualItems (). map (( virtualRow ) => {
const isLoaderRow = virtualRow . index > allRows . length - 1 ;
const post = allRows [ virtualRow . index ];
return (
< div
key = { virtualRow . index }
style = { {
position: "absolute" ,
top: 0 ,
left: 0 ,
width: "100%" ,
height: ` ${ virtualRow . size } px` ,
transform: `translateY( ${ virtualRow . start } px)` ,
} }
>
{ isLoaderRow ? "Loading..." : post . title }
</ div >
);
}) }
</ div >
</ div >
);
};
Refetching All Pages
const { query } = useInfiniteList ({
resource: "posts" ,
});
const handleRefresh = () => {
query . refetch (); // Refetches all loaded pages
};
Type Parameters
TQueryFnData - Result data type returned by the query function. Extends BaseRecord.
TError - Custom error type that extends HttpError.
TData - Result data type returned by the select function. Extends BaseRecord. Defaults to TQueryFnData.
FAQ
How do I get all items across all pages?
Flatten the pages array: const allItems = data ?. pages . flatMap (( page ) => page . data ) ?? [];
How do I implement 'Load More' button vs automatic infinite scroll?
Can I use this with virtual scrolling?
Yes, combine with @tanstack/react-virtual for efficient rendering of large lists. See the virtual scrolling example above.
How do I get the total count of items?
Access the total from the first page: const totalCount = data ?. pages [ 0 ]?. total ;
What happens when I refetch?
Calling refetch() will refetch all currently loaded pages, not just the first page.