useInfiniteQuery hook is used for fetching paginated or infinite scrolling data. It manages multiple pages of data and provides helpers for loading more pages.
Import
import { useInfiniteQuery } from '@tanstack/react-query'
Signature
function useInfiniteQuery<
TQueryFnData,
TError = DefaultError,
TData = InfiniteData<TQueryFnData>,
TQueryKey extends QueryKey = QueryKey,
TPageParam = unknown,
>(
options: UseInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>,
queryClient?: QueryClient,
): UseInfiniteQueryResult<TData, TError>
Parameters
The infinite query options object.
Show properties
Show properties
A unique key for the query. Must be an array.
queryKey: ['projects']
queryKey: ['projects', { status: 'active' }]
The function that fetches data for each page. Receives a
QueryFunctionContext with:queryKey: TQueryKey- The query keypageParam: TPageParam- The current page parametersignal: AbortSignal- AbortSignal for cancellationmeta: QueryMeta | undefined- Optional metadatadirection: FetchDirection- Direction of pagination ('forward'or'backward')
queryFn: async ({ pageParam }) => {
const response = await fetch(`/api/projects?cursor=${pageParam}`)
return response.json()
}
The default page parameter to use when fetching the first page.
initialPageParam: 0
initialPageParam: undefined
initialPageParam: null
getNextPageParam
(lastPage: TQueryFnData, allPages: TQueryFnData[], lastPageParam: TPageParam, allPageParams: TPageParam[]) => TPageParam | undefined | null
required
Function to determine the next page parameter. Return
undefined or null to indicate no more pages.getNextPageParam: (lastPage, allPages, lastPageParam) => lastPage.nextCursor
getNextPageParam: (lastPage) => lastPage.nextId ?? undefined
getPreviousPageParam
(firstPage: TQueryFnData, allPages: TQueryFnData[], firstPageParam: TPageParam, allPageParams: TPageParam[]) => TPageParam | undefined | null
Function to determine the previous page parameter. Return
undefined or null to indicate no more pages.getPreviousPageParam: (firstPage) => firstPage.prevCursor ?? undefined
Maximum number of pages to store in the data. When the maximum is reached, fetching a new page will remove the furthest page from the opposite direction.
maxPages: 3
Set to
false to disable automatic execution.The time in milliseconds after data is considered stale.
The time in milliseconds that unused/inactive cache data remains in memory.
If set to a number, the query will continuously refetch at this frequency.
If
true, refetch will continue while the tab is in the background.Refetch on window focus behavior.
Refetch on reconnect behavior.
Refetch on mount behavior.
Retry configuration for failed queries.
Delay between retries in milliseconds.
Transform or select part of the infinite data.
select: data => ({
pages: data.pages.map(page => page.items).flat(),
pageParams: data.pageParams,
})
Set to
true to throw errors to an error boundary.Network mode configuration.
Optional metadata.
Override the default QueryClient.
Returns
The infinite query result object.
Show properties
Show properties
The aggregated data object containing all pages and page parameters.
{
pages: TQueryFnData[],
pageParams: TPageParam[]
}
The error object for the query, if an error was thrown.
The status of the query.
The fetch status of the query.
true if the query is in pending state.true if the first fetch is in progress.true if the queryFn is executing.true if the query has succeeded.true if the query has errored.true if there is a next page to fetch (determined by getNextPageParam).true if there is a previous page to fetch (determined by getPreviousPageParam).true while fetching the next page with fetchNextPage.true while fetching the previous page with fetchPreviousPage.true if the query failed while fetching the next page.true if the query failed while fetching the previous page.Function to fetch the next page of results.
fetchNextPage({ cancelRefetch: true })
Function to fetch the previous page of results.
fetchPreviousPage({ cancelRefetch: true })
Function to manually refetch all pages.
Examples
Cursor-based Pagination
import { useInfiniteQuery } from '@tanstack/react-query'
interface Project {
id: number
name: string
}
interface ProjectsResponse {
data: Project[]
nextCursor?: number
}
function Projects() {
const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
} = useInfiniteQuery({
queryKey: ['projects'],
queryFn: async ({ pageParam }) => {
const response = await fetch(`/api/projects?cursor=${pageParam}`)
return response.json() as Promise<ProjectsResponse>
},
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.nextCursor,
})
return (
<div>
{data?.pages.map((page, i) => (
<div key={i}>
{page.data.map(project => (
<div key={project.id}>{project.name}</div>
))}
</div>
))}
<button
onClick={() => fetchNextPage()}
disabled={!hasNextPage || isFetchingNextPage}
>
{isFetchingNextPage
? 'Loading more...'
: hasNextPage
? 'Load More'
: 'Nothing more to load'}
</button>
</div>
)
}
Offset/Limit Pagination
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
queryKey: ['projects'],
queryFn: async ({ pageParam }) => {
const response = await fetch(
`/api/projects?limit=10&offset=${pageParam}`
)
return response.json()
},
initialPageParam: 0,
getNextPageParam: (lastPage, allPages, lastPageParam) => {
if (lastPage.length === 0) return undefined
return lastPageParam + 10
},
})
Bi-directional Pagination
const {
data,
fetchNextPage,
fetchPreviousPage,
hasNextPage,
hasPreviousPage,
} = useInfiniteQuery({
queryKey: ['projects'],
queryFn: async ({ pageParam }) => fetchProjects(pageParam),
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.nextCursor,
getPreviousPageParam: (firstPage) => firstPage.prevCursor,
})
With Max Pages
const { data } = useInfiniteQuery({
queryKey: ['projects'],
queryFn: fetchProjects,
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.nextCursor,
maxPages: 3, // Only keep 3 pages in memory
})
Flattening Pages with Select
const { data } = useInfiniteQuery({
queryKey: ['projects'],
queryFn: fetchProjects,
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.nextCursor,
select: data => ({
pages: data.pages.flatMap(page => page.data),
pageParams: data.pageParams,
}),
})
// data.pages is now a flat array of all items
Notes
The
initialPageParam is required and should match the type of your page parameters.Return
undefined or null from getNextPageParam to indicate there are no more pages.The
data object has a specific shape: { pages: TQueryFnData[], pageParams: TPageParam[] }