Skip to main content
The InfiniteQueryObserver extends QueryObserver to provide additional functionality for infinite queries (pagination). It powers hooks like useInfiniteQuery.

Constructor

Creates a new InfiniteQueryObserver instance.
const observer = new InfiniteQueryObserver<TQueryFnData, TError, TData, TQueryKey, TPageParam>(
  client: QueryClient,
  options: InfiniteQueryObserverOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>
)
client
QueryClient
required
The QueryClient instance to use.
options
InfiniteQueryObserverOptions
required
Options for the infinite query observer.

Example

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

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) => lastPage.nextCursor,
  getPreviousPageParam: (firstPage, allPages, firstPageParam) => firstPage.prevCursor,
})

Methods

InfiniteQueryObserver inherits all methods from QueryObserver and adds the following:

fetchNextPage

Fetches the next page of data.
fetchNextPage(
  options?: FetchNextPageOptions
): Promise<InfiniteQueryObserverResult<TData, TError>>
options
FetchNextPageOptions
Options for fetching the next page.
Promise<InfiniteQueryObserverResult>
Promise
Returns a promise that resolves with the infinite query result.

Example

const result = await observer.fetchNextPage()
console.log('Has next page:', result.hasNextPage)
console.log('All pages:', result.data.pages)

fetchPreviousPage

Fetches the previous page of data.
fetchPreviousPage(
  options?: FetchPreviousPageOptions
): Promise<InfiniteQueryObserverResult<TData, TError>>
options
FetchPreviousPageOptions
Options for fetching the previous page.
Promise<InfiniteQueryObserverResult>
Promise
Returns a promise that resolves with the infinite query result.

Example

const result = await observer.fetchPreviousPage()
console.log('Has previous page:', result.hasPreviousPage)

subscribe

Subscribes to the observer. Same as QueryObserver but with InfiniteQueryObserverResult.
subscribe(
  listener: (result: InfiniteQueryObserverResult<TData, TError>) => void
): () => void
listener
(result: InfiniteQueryObserverResult<TData, TError>) => void
required
Function called when the query result changes.
() => void
function
Returns an unsubscribe function.

Example

const unsubscribe = observer.subscribe((result) => {
  console.log('Pages:', result.data?.pages)
  console.log('Page params:', result.data?.pageParams)
  console.log('Has next page:', result.hasNextPage)
  console.log('Has previous page:', result.hasPreviousPage)
  console.log('Is fetching next page:', result.isFetchingNextPage)
})

unsubscribe()

getCurrentResult

Returns the current infinite query result.
getCurrentResult(): InfiniteQueryObserverResult<TData, TError>
InfiniteQueryObserverResult
InfiniteQueryObserverResult
Returns the current infinite query result.

setOptions

Updates the observer options. Automatically sets the infinite query behavior.
setOptions(
  options: InfiniteQueryObserverOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>
): void
options
InfiniteQueryObserverOptions
required
New options for the observer.

getOptimisticResult

Returns an optimistic infinite query result.
getOptimisticResult(
  options: DefaultedInfiniteQueryObserverOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>
): InfiniteQueryObserverResult<TData, TError>

InfiniteData Structure

The data returned by an infinite query has a special structure:
interface InfiniteData<TData, TPageParam> {
  pages: TData[]
  pageParams: TPageParam[]
}

Example

const result = observer.getCurrentResult()

if (result.data) {
  // Access all pages
  result.data.pages.forEach((page, index) => {
    console.log(`Page ${index}:`, page)
    console.log(`Page param:`, result.data.pageParams[index])
  })
  
  // Flatten all items from all pages
  const allItems = result.data.pages.flatMap(page => page.items)
}

Usage Example

Here’s a complete example:
import { QueryClient, InfiniteQueryObserver } from '@tanstack/query-core'

const queryClient = new QueryClient()

const observer = new InfiniteQueryObserver(queryClient, {
  queryKey: ['projects'],
  queryFn: async ({ pageParam }) => {
    const response = await fetch(
      `/api/projects?cursor=${pageParam}&limit=10`
    )
    return response.json()
  },
  initialPageParam: 0,
  getNextPageParam: (lastPage, allPages, lastPageParam) => {
    // Return undefined if no more pages
    return lastPage.nextCursor ?? undefined
  },
  getPreviousPageParam: (firstPage, allPages, firstPageParam) => {
    return firstPage.prevCursor ?? undefined
  },
})

const unsubscribe = observer.subscribe((result) => {
  if (result.isLoading) {
    console.log('Loading first page...')
  } else if (result.isError) {
    console.error('Error:', result.error)
  } else if (result.isSuccess) {
    console.log('Total pages loaded:', result.data.pages.length)
    
    if (result.hasNextPage && !result.isFetchingNextPage) {
      // Automatically fetch next page
      observer.fetchNextPage()
    }
  }
  
  if (result.isFetchingNextPage) {
    console.log('Loading more...')
  }
})

// Clean up
unsubscribe()
observer.destroy()

Differences from QueryObserver

  1. Data Structure: Returns InfiniteData<TData, TPageParam> instead of TData
  2. Additional Methods: fetchNextPage() and fetchPreviousPage()
  3. Additional Result Properties: hasNextPage, hasPreviousPage, isFetchingNextPage, etc.
  4. Required Options: Requires initialPageParam, getNextPageParam
  5. Automatic Behavior: Automatically applies infinite query behavior

Page Management

You can control the number of pages kept in memory using the maxPages option:
const observer = new InfiniteQueryObserver(queryClient, {
  queryKey: ['projects'],
  queryFn: fetchProjects,
  initialPageParam: 0,
  getNextPageParam: (lastPage) => lastPage.nextCursor,
  maxPages: 3, // Only keep 3 pages in memory
})
When maxPages is set, the oldest pages will be removed as new pages are fetched.

Build docs developers (and LLMs) love