createInfiniteQuery function. It manages multiple pages of data and provides methods to load more pages in Svelte 5 applications.
Signature
function createInfiniteQuery<TQueryFnData, TError, TData, TQueryKey, TPageParam>(
options: Accessor<CreateInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>>,
queryClient?: Accessor<QueryClient>,
): CreateInfiniteQueryResult<TData, TError>
Parameters
options
Accessor<CreateInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>>
required
A function (accessor) returning infinite query configuration options.
Show properties
Show properties
Unique identifier for the query.
Function that fetches a page of data. Receives
pageParam in context.The initial page parameter for the first page.
getNextPageParam
(lastPage: TQueryFnData, allPages: TQueryFnData[], lastPageParam: TPageParam, allPageParams: TPageParam[]) => TPageParam | undefined | null
required
Function to determine the next page parameter. Return
undefined or null when there are no more pages.getPreviousPageParam
(firstPage: TQueryFnData, allPages: TQueryFnData[], firstPageParam: TPageParam, allPageParams: TPageParam[]) => TPageParam | undefined | null
Function to determine the previous page parameter for bi-directional pagination.
Set to
false to disable automatic query execution.Time in milliseconds until cached data is considered stale.
Time in milliseconds before unused data is garbage collected.
Refetch when window regains focus.
Transform or select a part of the data.
Accessor returning a custom QueryClient instance. If not provided, uses the client from context.
Returns
Reactive infinite query state and methods.
Show properties
Show properties
Object containing all pages of data with
pages and pageParams arrays.The error object if the query failed.
true when fetching the first page for the first time.true whenever the query is fetching (including additional pages).true when fetching the next page.true when fetching the previous page.true if there is a next page to fetch.true if there is a previous page to fetch.Fetch the next page of data.
Fetch the previous page of data.
true when the query has successfully fetched data.true when the query encountered an error.The current status of the query.
Manually trigger a refetch of all pages.
Type Parameters
TQueryFnData- Type of data returned by each pageTError- Type of error (defaults toDefaultError)TData- Type of final data (defaults toInfiniteData<TQueryFnData>)TQueryKey- Type of the query key (defaults toQueryKey)TPageParam- Type of page parameter (defaults tounknown)
Examples
Basic Infinite Scroll (Svelte 5 Runes)
<script lang="ts">
import { createInfiniteQuery } from '@tanstack/svelte-query'
const query = createInfiniteQuery(() => ({
queryKey: ['posts'],
queryFn: async ({ pageParam }) => {
const res = await fetch(`/api/posts?page=${pageParam}`)
return res.json()
},
initialPageParam: 0,
getNextPageParam: (lastPage, allPages) => {
return lastPage.hasMore ? allPages.length : undefined
},
}))
function loadMore() {
query.fetchNextPage()
}
</script>
{#each query.data?.pages ?? [] as page}
{#each page.posts as post}
<div>
<h3>{post.title}</h3>
<p>{post.body}</p>
</div>
{/each}
{/each}
<button
onclick={loadMore}
disabled={!query.hasNextPage || query.isFetchingNextPage}
>
{#if query.isFetchingNextPage}
Loading more...
{:else if query.hasNextPage}
Load More
{:else}
No more posts
{/if}
</button>
Cursor-Based Pagination
<script lang="ts">
import { createInfiniteQuery } from '@tanstack/svelte-query'
const query = createInfiniteQuery(() => ({
queryKey: ['posts'],
queryFn: async ({ pageParam }) => {
const res = await fetch(`/api/posts?cursor=${pageParam}`)
return res.json()
},
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.nextCursor,
}))
</script>
Bi-directional Pagination
<script lang="ts">
import { createInfiniteQuery } from '@tanstack/svelte-query'
const query = createInfiniteQuery(() => ({
queryKey: ['posts'],
queryFn: async ({ pageParam }) => {
const res = await fetch(`/api/posts?cursor=${pageParam}`)
return res.json()
},
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.nextCursor,
getPreviousPageParam: (firstPage) => firstPage.prevCursor,
}))
</script>
<button
onclick={() => query.fetchPreviousPage()}
disabled={!query.hasPreviousPage || query.isFetchingPreviousPage}
>
Load Previous
</button>
<!-- Posts display -->
<button
onclick={() => query.fetchNextPage()}
disabled={!query.hasNextPage || query.isFetchingNextPage}
>
Load Next
</button>
With TypeScript
<script lang="ts">
import { createInfiniteQuery } from '@tanstack/svelte-query'
interface Post {
id: number
title: string
body: string
}
interface PostsPage {
posts: Post[]
nextCursor?: number
hasMore: boolean
}
const query = createInfiniteQuery(() => ({
queryKey: ['posts'],
queryFn: async ({ pageParam }): Promise<PostsPage> => {
const res = await fetch(`/api/posts?cursor=${pageParam}`)
return res.json()
},
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.nextCursor,
}))
// query.data.pages is typed as PostsPage[]
</script>
Reactive Query Parameters
<script lang="ts">
import { createInfiniteQuery } from '@tanstack/svelte-query'
let filter = $state('all')
const query = createInfiniteQuery(() => ({
queryKey: ['posts', filter],
queryFn: async ({ pageParam }) => {
const res = await fetch(
`/api/posts?filter=${filter}&cursor=${pageParam}`
)
return res.json()
},
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.nextCursor,
}))
</script>
<select bind:value={filter}>
<option value="all">All</option>
<option value="active">Active</option>
<option value="completed">Completed</option>
</select>
Infinite Scroll with Intersection Observer
<script lang="ts">
import { createInfiniteQuery } from '@tanstack/svelte-query'
import { onMount } from 'svelte'
let loadMoreRef: HTMLDivElement
const query = createInfiniteQuery(() => ({
queryKey: ['posts'],
queryFn: fetchPosts,
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.nextCursor,
}))
onMount(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (
entry.isIntersecting &&
query.hasNextPage &&
!query.isFetchingNextPage
) {
query.fetchNextPage()
}
},
{ threshold: 1.0 }
)
if (loadMoreRef) {
observer.observe(loadMoreRef)
}
return () => observer.disconnect()
})
</script>
<div>
{#each query.data?.pages ?? [] as page}
{#each page.posts as post}
<div>{post.title}</div>
{/each}
{/each}
{#if query.hasNextPage}
<div bind:this={loadMoreRef}>
{#if query.isFetchingNextPage}
Loading...
{/if}
</div>
{/if}
</div>
Refetch All Pages
<script lang="ts">
import { createInfiniteQuery } from '@tanstack/svelte-query'
const query = createInfiniteQuery(() => ({
queryKey: ['posts'],
queryFn: fetchPosts,
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.nextCursor,
}))
function refreshAll() {
query.refetch() // Refetches all loaded pages
}
</script>
<button onclick={refreshAll}>Refresh All</button>
With Loading States
<script lang="ts">
import { createInfiniteQuery } from '@tanstack/svelte-query'
const query = createInfiniteQuery(() => ({
queryKey: ['posts'],
queryFn: fetchPosts,
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.nextCursor,
}))
</script>
{#if query.isLoading}
<div>Loading first page...</div>
{:else if query.isError}
<div>Error: {query.error?.message}</div>
{:else if query.isSuccess}
{#each query.data.pages as page}
{#each page.posts as post}
<div>{post.title}</div>
{/each}
{/each}
{#if query.hasNextPage}
<button
onclick={() => query.fetchNextPage()}
disabled={query.isFetchingNextPage}
>
{query.isFetchingNextPage ? 'Loading...' : 'Load More'}
</button>
{/if}
{/if}
Page-Based Pagination
<script lang="ts">
import { createInfiniteQuery } from '@tanstack/svelte-query'
const query = createInfiniteQuery(() => ({
queryKey: ['posts'],
queryFn: async ({ pageParam }) => {
const res = await fetch(`/api/posts?page=${pageParam}&limit=10`)
return res.json()
},
initialPageParam: 1,
getNextPageParam: (lastPage, allPages, lastPageParam) => {
return lastPage.hasMore ? lastPageParam + 1 : undefined
},
getPreviousPageParam: (firstPage, allPages, firstPageParam) => {
return firstPageParam > 1 ? firstPageParam - 1 : undefined
},
}))
</script>
Search with Infinite Scroll
<script lang="ts">
import { createInfiniteQuery } from '@tanstack/svelte-query'
let searchTerm = $state('')
let debouncedSearch = $state('')
// Debounce search
$effect(() => {
const timeout = setTimeout(() => {
debouncedSearch = searchTerm
}, 300)
return () => clearTimeout(timeout)
})
const query = createInfiniteQuery(() => ({
queryKey: ['posts', 'search', debouncedSearch],
queryFn: async ({ pageParam }) => {
const res = await fetch(
`/api/posts?search=${debouncedSearch}&cursor=${pageParam}`
)
return res.json()
},
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.nextCursor,
enabled: debouncedSearch.length > 0,
}))
</script>
<input
type="text"
bind:value={searchTerm}
placeholder="Search posts..."
/>
{#if query.data}
{#each query.data.pages as page}
{#each page.posts as post}
<div>{post.title}</div>
{/each}
{/each}
{/if}
Notes
Svelte Query uses Svelte 5’s runes mode. The options parameter must be an accessor (function) to track reactive dependencies.
initialPageParam is required in v5. Make sure to provide it when creating infinite queries.When using
refetch(), all pages will be refetched. For better UX, consider using invalidateQueries to only refetch when needed.Related
- createQuery - For non-paginated data
- Infinite Queries Guide - Detailed guide on infinite queries
- Svelte Guide - Complete guide to Svelte Query