Skip to main content

Overview

The InfiniteQueryObserver extends QueryObserver to provide support for infinite/paginated queries. It adds methods for fetching next and previous pages and tracks pagination state.

Constructor

const observer = new InfiniteQueryObserver<TQueryFnData, TError, TData, TQueryKey, TPageParam>(
  client: QueryClient,
  options: InfiniteQueryObserverOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>
)
client
QueryClient
required
The QueryClient instance
options
InfiniteQueryObserverOptions
required
Options for the infinite query observer
Example:
const observer = new InfiniteQueryObserver(queryClient, {
  queryKey: ['projects'],
  queryFn: async ({ pageParam }) => {
    const response = await fetch(`/api/projects?cursor=${pageParam}`)
    return response.json()
  },
  initialPageParam: 0,
  getNextPageParam: (lastPage, allPages, lastPageParam) => {
    return lastPage.nextCursor
  },
  getPreviousPageParam: (firstPage, allPages, firstPageParam) => {
    return firstPage.prevCursor
  },
})

Methods

InfiniteQueryObserver inherits all methods from QueryObserver and adds:

fetchNextPage()

Fetches the next page of data.
const result = await observer.fetchNextPage(
  options?: FetchNextPageOptions
): Promise<InfiniteQueryObserverResult<TData, TError>>
options
FetchNextPageOptions
Returns: Promise resolving to the infinite query result Example:
const result = await observer.fetchNextPage()
if (result.hasNextPage) {
  console.log('More pages available')
}

fetchPreviousPage()

Fetches the previous page of data.
const result = await observer.fetchPreviousPage(
  options?: FetchPreviousPageOptions
): Promise<InfiniteQueryObserverResult<TData, TError>>
options
FetchPreviousPageOptions
Returns: Promise resolving to the infinite query result Example:
const result = await observer.fetchPreviousPage()
if (result.hasPreviousPage) {
  console.log('More previous pages available')
}

subscribe()

Subscribes to infinite query updates.
const unsubscribe = observer.subscribe(
  listener: (result: InfiniteQueryObserverResult<TData, TError>) => void
): () => void
listener
(result: InfiniteQueryObserverResult<TData, TError>) => void
required
Callback function that receives infinite query results
Returns: Function to unsubscribe

getCurrentResult()

Returns the current infinite query result without subscribing.
const result = observer.getCurrentResult(): InfiniteQueryObserverResult<TData, TError>
Returns: Current infinite query result
InfiniteQueryObserverResult
object
Extends all properties from QueryObserverResult and adds:

Type Parameters

  • TQueryFnData - The type of data returned by the query function for a single page
  • TError - The type of error that can be thrown (defaults to DefaultError)
  • TData - The type of data after selection/transformation
  • TQueryKey - The type of the query key
  • TPageParam - The type of the page parameter

Usage Example

import { QueryClient, InfiniteQueryObserver } from '@tanstack/query-core'

const queryClient = new QueryClient()

interface Project {
  id: number
  name: string
}

interface ProjectsResponse {
  projects: Project[]
  nextCursor?: number
  prevCursor?: number
}

const observer = new InfiniteQueryObserver<
  ProjectsResponse,
  Error,
  InfiniteData<ProjectsResponse>,
  ['projects'],
  number
>(queryClient, {
  queryKey: ['projects'],
  queryFn: async ({ pageParam }) => {
    const response = await fetch(`/api/projects?cursor=${pageParam}`)
    if (!response.ok) throw new Error('Failed to fetch')
    return response.json()
  },
  initialPageParam: 0,
  getNextPageParam: (lastPage) => lastPage.nextCursor,
  getPreviousPageParam: (firstPage) => firstPage.prevCursor,
})

// Subscribe to updates
const unsubscribe = observer.subscribe((result) => {
  if (result.isLoading) {
    console.log('Loading...')
  } else if (result.isSuccess) {
    console.log('Pages:', result.data.pages.length)
    console.log('Has next page:', result.hasNextPage)
    
    // Flatten all projects
    const allProjects = result.data.pages.flatMap(page => page.projects)
    console.log('Total projects:', allProjects.length)
  }
})

// Load more data
if (observer.getCurrentResult().hasNextPage) {
  await observer.fetchNextPage()
}

// Later, clean up
unsubscribe()
observer.destroy()

Infinite Data Structure

The data returned by an infinite query has a specific structure:
interface InfiniteData<TData, TPageParam> {
  pages: TData[]         // Array of page data
  pageParams: TPageParam[] // Array of page parameters
}
Example:
// After fetching 3 pages
const result = observer.getCurrentResult()

console.log(result.data)
// {
//   pages: [
//     { projects: [...], nextCursor: 10 },
//     { projects: [...], nextCursor: 20 },
//     { projects: [...], nextCursor: 30 },
//   ],
//   pageParams: [0, 10, 20]
// }

Page Parameter Functions

The getNextPageParam and getPreviousPageParam functions determine if there are more pages to fetch:
getNextPageParam: (lastPage, allPages, lastPageParam, allPageParams) => {
  // Return undefined/null when there are no more pages
  if (!lastPage.nextCursor) return undefined
  return lastPage.nextCursor
}
Returning undefined or null from these functions sets hasNextPage or hasPreviousPage to false.

Build docs developers (and LLMs) love