API Reference

useInfiniteQuery

Call Signature

ts
function useInfiniteQuery<TQueryFnData, TError, TData, TQueryKey, TPageParam>(options, queryClient?): UseInfiniteQueryReturnType<TData, TError>;

Defined in: vue-query/src/useInfiniteQuery.ts:117

The options for useInfiniteQuery are identical to useQuery, with the addition of initialPageParam, getNextPageParam, getPreviousPageParam, and maxPages.

This overload is selected when initialData is set, so the resulting data is never undefined.

enabled tracks reactive dependencies automatically as a ref, a plain value, or a reactive getter (() => ...). queryKey reacts through a ref for the array itself, or refs and reactive getters as individual entries — the array itself can't be a bare getter.

Type Parameters

TQueryFnData

TQueryFnData

TError

TError = Error

TData

TData = InfiniteData<TQueryFnData, unknown>

TQueryKey

TQueryKey extends readonly unknown[] = readonly unknown[]

TPageParam

TPageParam = unknown

Parameters

options

MaybeRefOrGetter<DefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>>

The DefinedInitialDataInfiniteOptions to use — everything you can pass to useInfiniteQuery, with initialData set.

queryClient?

QueryClient

Use this to use a custom QueryClient. Otherwise, the one provided by VueQueryPlugin will be used.

Returns

UseInfiniteQueryReturnType<TData, TError>

The same properties as useQuery, with the addition of fetchNextPage, fetchPreviousPage, hasNextPage, hasPreviousPage, isFetchingNextPage, and isFetchingPreviousPage. data.value.pages and data.value.pageParams are also added, as long as a select doesn't change TData away from its default InfiniteData<TQueryFnData> shape.

Remarks

Keep in mind that imperative fetch calls, such as fetchNextPage, may interfere with the default refetch behavior, resulting in outdated data. Make sure to call these functions only in response to user actions, or add conditions like hasNextPage && !isFetching.

Example

vue
<script setup lang="ts">
import { useInfiniteQuery } from '@tanstack/vue-query'

// `data` is never `undefined`, thanks to `initialData` — even if a refetch fails, so the
// list stays visible alongside the error.
const { data, isError, error } = useInfiniteQuery({
  queryKey: ['projects'],
  queryFn: ({ pageParam }) => fetchProjects(pageParam),
  initialPageParam: 0,
  getNextPageParam: (lastPage) => lastPage.nextId,
  initialData: { pages: [], pageParams: [] },
})
</script>

<template>
  <span v-if="isError">Error: {{ error.message }}</span>
  <ul>
    <template v-for="page in data.pages" :key="page.nextId">
      <li v-for="project in page.projects" :key="project.id">{{ project.name }}</li>
    </template>
  </ul>
</template>

Call Signature

ts
function useInfiniteQuery<TQueryFnData, TError, TData, TQueryKey, TPageParam>(options, queryClient?): UseInfiniteQueryReturnType<TData, TError>;

Defined in: vue-query/src/useInfiniteQuery.ts:251

The options for useInfiniteQuery are identical to useQuery, with the addition of initialPageParam, getNextPageParam, getPreviousPageParam, and maxPages.

enabled tracks reactive dependencies automatically as a ref, a plain value, or a reactive getter (() => ...). queryKey reacts through a ref for the array itself, or refs and reactive getters as individual entries — the array itself can't be a bare getter.

Type Parameters

TQueryFnData

TQueryFnData

TError

TError = Error

TData

TData = InfiniteData<TQueryFnData, unknown>

TQueryKey

TQueryKey extends readonly unknown[] = readonly unknown[]

TPageParam

TPageParam = unknown

Parameters

options

MaybeRefOrGetter<UndefinedInitialDataInfiniteOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>>

The UndefinedInitialDataInfiniteOptions to use — everything you can pass to useInfiniteQuery.

queryClient?

QueryClient

Use this to use a custom QueryClient. Otherwise, the one provided by VueQueryPlugin will be used.

Returns

UseInfiniteQueryReturnType<TData, TError>

The same properties as useQuery, with the addition of fetchNextPage, fetchPreviousPage, hasNextPage, hasPreviousPage, isFetchingNextPage, and isFetchingPreviousPage. data.value.pages and data.value.pageParams are also added, as long as a select doesn't change TData away from its default InfiniteData<TQueryFnData> shape.

Remarks

Keep in mind that imperative fetch calls, such as fetchNextPage, may interfere with the default refetch behavior, resulting in outdated data. Make sure to call these functions only in response to user actions, or add conditions like hasNextPage && !isFetching.

Examples

Fetching the next page from a "Load More" button click:

vue
<script setup lang="ts">
import { useInfiniteQuery } from '@tanstack/vue-query'

const {
  data,
  isPending,
  isError,
  error,
  fetchNextPage,
  hasNextPage,
  isFetching,
  isFetchingNextPage,
} = useInfiniteQuery({
  queryKey: ['projects'],
  queryFn: ({ pageParam }) => fetchProjects(pageParam),
  initialPageParam: 0,
  getNextPageParam: (lastPage) => lastPage.nextId,
})
</script>

<template>
  <span v-if="isPending">Loading...</span>
  <span v-else-if="isError">Error: {{ error.message }}</span>
  <template v-else>
    <ul>
      <template v-for="page in data.pages" :key="page.nextId">
        <li v-for="project in page.projects" :key="project.id">{{ project.name }}</li>
      </template>
    </ul>
    <button @click="fetchNextPage()" :disabled="!hasNextPage || isFetching">
      {{ isFetchingNextPage ? 'Loading more...' : hasNextPage ? 'Load More' : 'Nothing more to load' }}
    </button>
  </template>
</template>

Fetching the next page automatically as the user scrolls, using an IntersectionObserver on a sentinel element after the list:

vue
<script setup lang="ts">
import { onUnmounted, ref, watch } from 'vue'
import { useInfiniteQuery } from '@tanstack/vue-query'

const {
  data,
  isPending,
  isError,
  error,
  fetchNextPage,
  hasNextPage,
  isFetching,
  isFetchingNextPage,
} = useInfiniteQuery({
  queryKey: ['projects'],
  queryFn: ({ pageParam }) => fetchProjects(pageParam),
  initialPageParam: 0,
  getNextPageParam: (lastPage) => lastPage.nextId,
})

const sentinel = ref<HTMLElement>()
let observer: IntersectionObserver | undefined

watch(sentinel, (el) => {
  observer?.disconnect()
  if (el == null) return

  observer = new IntersectionObserver(([entry]) => {
    if (entry?.isIntersecting && hasNextPage.value && !isFetching.value) {
      fetchNextPage()
    }
  })
  observer.observe(el)
})

onUnmounted(() => observer?.disconnect())
</script>

<template>
  <span v-if="isPending">Loading...</span>
  <span v-else-if="isError">Error: {{ error.message }}</span>
  <template v-else>
    <ul>
      <template v-for="page in data.pages" :key="page.nextId">
        <li v-for="project in page.projects" :key="project.id">{{ project.name }}</li>
      </template>
    </ul>
    <div ref="sentinel">{{ isFetchingNextPage ? 'Loading more...' : '' }}</div>
  </template>
</template>

Call Signature

ts
function useInfiniteQuery<TQueryFnData, TError, TData, TQueryKey, TPageParam>(options, queryClient?): UseInfiniteQueryReturnType<TData, TError>;

Defined in: vue-query/src/useInfiniteQuery.ts:347

Fallback overload for options whose initialData presence isn't statically known — for example, a ref/reactive object built up conditionally, rather than a plain object literal. Prefer one of the other overloads when possible, since they infer whether data can be undefined from initialData directly.

enabled tracks reactive dependencies automatically as a ref, a plain value, or a reactive getter (() => ...). queryKey reacts through a ref for the array itself, or refs and reactive getters as individual entries — the array itself can't be a bare getter.

Type Parameters

TQueryFnData

TQueryFnData

TError

TError = Error

TData

TData = InfiniteData<TQueryFnData, unknown>

TQueryKey

TQueryKey extends readonly unknown[] = readonly unknown[]

TPageParam

TPageParam = unknown

Parameters

options

MaybeRefOrGetter<UseInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>>

A ref, plain value, or reactive getter resolving to the UseInfiniteQueryOptions to use.

queryClient?

QueryClient

Use this to use a custom QueryClient. Otherwise, the one provided by VueQueryPlugin will be used.

Returns

UseInfiniteQueryReturnType<TData, TError>

The same properties as useQuery, with the addition of fetchNextPage, fetchPreviousPage, hasNextPage, hasPreviousPage, isFetchingNextPage, and isFetchingPreviousPage.

Examples

Passing a whole-options getter so maxPages reacts to a setting stored elsewhere, not just queryKey:

vue
<script setup lang="ts">
import { useInfiniteQuery } from '@tanstack/vue-query'

const props = defineProps<{ projectId: number; maxPages: number }>()

const { data } = useInfiniteQuery(() => ({
  queryKey: ['project', props.projectId, 'issues'],
  queryFn: ({ pageParam }) => fetchIssues(props.projectId, pageParam),
  initialPageParam: 0,
  getNextPageParam: (lastPage) => lastPage.nextId,
  maxPages: props.maxPages,
}))
</script>

<template>
  <template v-for="page in data?.pages" :key="page.nextId">
    <li v-for="issue in page.issues" :key="issue.id">{{ issue.title }}</li>
  </template>
</template>

A query that's disabled, type-safe, until postId is set — pass skipToken as queryFn instead of setting enabled: false. This requires a whole-options getter: queryFn isn't itself reactive, so the getter is what re-evaluates it on every change to props.postId:

vue
<script setup lang="ts">
import { skipToken, useInfiniteQuery } from '@tanstack/vue-query'

const props = defineProps<{ postId: string | undefined }>()

// Use `isLoading`, not `isPending`, so the loading state doesn't show while the query is disabled.
const { data, isLoading, isError, error } = useInfiniteQuery(() => {
  const postId = props.postId
  return {
    queryKey: ['post', postId, 'comments'],
    queryFn:
      postId != null
        ? ({ pageParam }) => fetchComments(postId, pageParam)
        : skipToken,
    initialPageParam: 0,
    getNextPageParam: (lastPage) => lastPage.nextId,
  }
})
</script>

<template>
  <span v-if="props.postId == null">Select a post</span>
  <span v-else-if="isLoading">Loading...</span>
  <span v-else-if="isError">Error: {{ error.message }}</span>
  <ul v-else>
    <template v-for="page in data.pages" :key="page.nextId">
      <li v-for="comment in page.comments" :key="comment.id">{{ comment.text }}</li>
    </template>
  </ul>
</template>