infiniteQueryOptions
The infiniteQueryOptions helper provides type-safe infinite query options with proper type inference. It’s a lightweight identity function that helps TypeScript infer types correctly and provides better autocomplete for infinite queries.
Import
import { infiniteQueryOptions } from '@tanstack/react-query'
Signature
function infiniteQueryOptions<
TQueryFnData,
TError = DefaultError,
TData = InfiniteData<TQueryFnData>,
TQueryKey extends QueryKey = QueryKey,
TPageParam = unknown,
>(
options: UseInfiniteQueryOptions<
TQueryFnData,
TError,
TData,
TQueryKey,
TPageParam
>
): UseInfiniteQueryOptions<
TQueryFnData,
TError,
TData,
TQueryKey,
TPageParam
> & {
queryKey: DataTag<TQueryKey, InfiniteData<TQueryFnData>, TError>
}
Type Parameters
The type of data returned by the query function for each page
TError
type
default:"DefaultError"
The type of error that can be thrown by the query function
TData
type
default:"InfiniteData<TQueryFnData>"
The type of data after transformation by the select function
The type of the query key
The type of the page parameter
Parameters
options
UseInfiniteQueryOptions
required
Infinite query configuration options. Accepts all options that useInfiniteQuery accepts.A unique key for the query
queryFn
QueryFunction<TQueryFnData, TQueryKey>
required
The function that fetches a page of data
The initial page parameter
getNextPageParam
(lastPage: TQueryFnData, allPages: TQueryFnData[], lastPageParam: TPageParam, allPageParams: TPageParam[]) => TPageParam | undefined | null
required
Function to get the next page parameter
getPreviousPageParam
(firstPage: TQueryFnData, allPages: TQueryFnData[], firstPageParam: TPageParam, allPageParams: TPageParam[]) => TPageParam | undefined | null
Function to get the previous page parameter
Returns
Returns the same options object that was passed in, with enhanced type information for better TypeScript inference.
Examples
Basic Usage
import { infiniteQueryOptions, useInfiniteQuery } from '@tanstack/react-query'
const projectsInfiniteOptions = infiniteQueryOptions({
queryKey: ['projects'],
queryFn: async ({ pageParam }) => {
const res = await fetch(`/api/projects?cursor=${pageParam}`)
return res.json()
},
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.nextCursor,
})
function Projects() {
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery(
projectsInfiniteOptions
)
return <div>{/* render pages */}</div>
}
Reusable Infinite Query Definitions
import { infiniteQueryOptions, useInfiniteQuery } from '@tanstack/react-query'
interface Project {
id: number
name: string
}
interface ProjectsResponse {
projects: Project[]
nextCursor?: number
}
const projectQueries = {
all: infiniteQueryOptions({
queryKey: ['projects'],
queryFn: async ({ pageParam }): Promise<ProjectsResponse> => {
const res = await fetch(`/api/projects?cursor=${pageParam}`)
return res.json()
},
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.nextCursor,
getPreviousPageParam: (firstPage) => firstPage.previousCursor,
}),
byCategory: (category: string) => infiniteQueryOptions({
queryKey: ['projects', category],
queryFn: async ({ pageParam }) => {
const res = await fetch(
`/api/projects?category=${category}&cursor=${pageParam}`
)
return res.json()
},
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.nextCursor,
}),
}
// Use in components
function Projects() {
const { data } = useInfiniteQuery(projectQueries.all)
return <div>{/* ... */}</div>
}
function CategoryProjects({ category }: { category: string }) {
const { data } = useInfiniteQuery(projectQueries.byCategory(category))
return <div>{/* ... */}</div>
}
With Type Inference
import { infiniteQueryOptions, useInfiniteQuery } from '@tanstack/react-query'
interface Post {
id: number
title: string
body: string
}
interface PostsPage {
posts: Post[]
nextCursor?: number
}
const postsInfiniteOptions = infiniteQueryOptions({
queryKey: ['posts'],
queryFn: async ({ pageParam }): Promise<PostsPage> => {
const res = await fetch(`/api/posts?cursor=${pageParam}`)
return res.json()
},
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.nextCursor,
})
function Posts() {
// TypeScript knows the structure of data
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery(
postsInfiniteOptions
)
return (
<div>
{data?.pages.map((page) => (
page.posts.map((post) => (
<div key={post.id}>{post.title}</div>
))
))}
{hasNextPage && (
<button onClick={() => fetchNextPage()}>Load More</button>
)}
</div>
)
}
With Select Function
import { infiniteQueryOptions, useInfiniteQuery } from '@tanstack/react-query'
const flatPostsOptions = infiniteQueryOptions({
queryKey: ['posts'],
queryFn: ({ pageParam }) => fetchPostsPage(pageParam),
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.nextCursor,
select: (data) => data.pages.flatMap((page) => page.posts),
})
function Posts() {
// data is now a flat array of posts
const { data } = useInfiniteQuery(flatPostsOptions)
return <div>{/* ... */}</div>
}
With QueryClient Methods
import {
infiniteQueryOptions,
useQueryClient,
useInfiniteQuery,
} from '@tanstack/react-query'
const projectsOptions = infiniteQueryOptions({
queryKey: ['projects'],
queryFn: ({ pageParam }) => fetchProjects(pageParam),
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.nextCursor,
})
function Projects() {
const queryClient = useQueryClient()
const { data } = useInfiniteQuery(projectsOptions)
const handleInvalidate = () => {
// Type-safe invalidation
queryClient.invalidateQueries(projectsOptions)
}
const handlePrefetch = () => {
// Type-safe prefetching
queryClient.prefetchInfiniteQuery(projectsOptions)
}
return <div>{/* ... */}</div>
}
import { infiniteQueryOptions, useInfiniteQuery } from '@tanstack/react-query'
const messagesOptions = infiniteQueryOptions({
queryKey: ['messages'],
queryFn: async ({ pageParam }) => {
const res = await fetch(`/api/messages?cursor=${pageParam}`)
return res.json()
},
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.nextCursor,
getPreviousPageParam: (firstPage) => firstPage.previousCursor,
})
function Messages() {
const {
data,
fetchNextPage,
fetchPreviousPage,
hasNextPage,
hasPreviousPage,
} = useInfiniteQuery(messagesOptions)
return (
<div>
{hasPreviousPage && (
<button onClick={() => fetchPreviousPage()}>Load Older</button>
)}
{data?.pages.map((page) => (
<div key={page.id}>{/* render messages */}</div>
))}
{hasNextPage && (
<button onClick={() => fetchNextPage()}>Load Newer</button>
)}
</div>
)
}
Notes
infiniteQueryOptions is an identity function that returns the exact object passed to it.
- Its primary purpose is to improve TypeScript type inference and provide better autocomplete.
- It’s particularly useful when you want to share infinite query configurations across multiple components or files.
- The function adds type annotations to the
queryKey that can be used for type-safe query invalidation and prefetching.
- Always provide
initialPageParam and getNextPageParam for infinite queries.
- Use
getPreviousPageParam if you need bidirectional pagination.