createInfiniteQuery primitive. It manages multiple pages of data and provides methods to load more pages in SolidJS applications.
Signature
function createInfiniteQuery<TQueryFnData, TError, TData, TQueryKey, TPageParam>(
options: Accessor<CreateInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>>,
queryClient?: Accessor<QueryClient>,
): CreateInfiniteQueryResult<TData, TError>
Parameters
Accessor<CreateInfiniteQueryOptions<TQueryFnData, TError, TData, TQueryKey, TPageParam>>
required
A Solid accessor (function) returning infinite query configuration options.
Show properties
Show properties
QueryKey
required
Unique identifier for the query.
(context: QueryFunctionContext<TQueryKey, TPageParam>) => Promise<TQueryFnData>
required
Function that fetches a page of data. Receives
pageParam in context.TPageParam
required
The initial page parameter for the first page.
(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.(firstPage: TQueryFnData, allPages: TQueryFnData[], firstPageParam: TPageParam, allPageParams: TPageParam[]) => TPageParam | undefined | null
Function to determine the previous page parameter for bi-directional pagination.
boolean
Set to
false to disable automatic query execution.number
Time in milliseconds until cached data is considered stale.
number
Time in milliseconds before unused data is garbage collected.
boolean
Refetch when window regains focus.
(data: InfiniteData<TQueryFnData, TPageParam>) => TData
Transform or select a part of the data.
Accessor<QueryClient>
Accessor returning a custom QueryClient instance. If not provided, uses the client from context.
Returns
object
Reactive infinite query state and methods.
Show properties
Show properties
InfiniteData<TData, TPageParam> | undefined
Object containing all pages of data with
pages and pageParams arrays.TError | null
The error object if the query failed.
boolean
true when fetching the first page for the first time.boolean
true whenever the query is fetching (including additional pages).boolean
true when fetching the next page.boolean
true when fetching the previous page.boolean
true if there is a next page to fetch.boolean
true if there is a previous page to fetch.(options?: FetchNextPageOptions) => Promise<InfiniteQueryObserverResult>
Fetch the next page of data.
(options?: FetchPreviousPageOptions) => Promise<InfiniteQueryObserverResult>
Fetch the previous page of data.
boolean
true when the query has successfully fetched data.boolean
true when the query encountered an error.'pending' | 'error' | 'success'
The current status of the query.
() => Promise<InfiniteQueryObserverResult>
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
import { createInfiniteQuery } from '@tanstack/solid-query'
import { For, Show } from 'solid-js'
function PostList() {
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
},
}))
return (
<div>
<For each={query.data?.pages}>
{(page) => (
<For each={page.posts}>
{(post) => (
<div>
<h3>{post.title}</h3>
<p>{post.body}</p>
</div>
)}
</For>
)}
</For>
<button
onClick={() => query.fetchNextPage()}
disabled={!query.hasNextPage || query.isFetchingNextPage}
>
<Show when={query.isFetchingNextPage} fallback="Load More">
Loading more...
</Show>
</button>
</div>
)
}
Cursor-Based Pagination
import { createInfiniteQuery } from '@tanstack/solid-query'
function PostList() {
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,
}))
return <div>{/* ... */}</div>
}
Bi-directional Pagination
import { createInfiniteQuery } from '@tanstack/solid-query'
import { Show } from 'solid-js'
function PostList() {
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,
}))
return (
<div>
<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>
</div>
)
}
With TypeScript
import { createInfiniteQuery } from '@tanstack/solid-query'
interface Post {
id: number
title: string
body: string
}
interface PostsPage {
posts: Post[]
nextCursor?: number
hasMore: boolean
}
function PostList() {
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[]
return <div>{/* ... */}</div>
}
Reactive Query Parameters
import { createSignal } from 'solid-js'
import { createInfiniteQuery } from '@tanstack/solid-query'
function PostList() {
const [filter, setFilter] = createSignal('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,
}))
return (
<div>
<select value={filter()} onChange={(e) => setFilter(e.target.value)}>
<option value="all">All</option>
<option value="active">Active</option>
<option value="completed">Completed</option>
</select>
{/* Posts display */}
</div>
)
}
Infinite Scroll with Intersection Observer
import { createInfiniteQuery } from '@tanstack/solid-query'
import { createEffect, onCleanup } from 'solid-js'
function PostList() {
let loadMoreRef: HTMLDivElement | undefined
const query = createInfiniteQuery(() => ({
queryKey: ['posts'],
queryFn: fetchPosts,
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.nextCursor,
}))
createEffect(() => {
if (!loadMoreRef) return
const observer = new IntersectionObserver(
([entry]) => {
if (
entry.isIntersecting &&
query.hasNextPage &&
!query.isFetchingNextPage
) {
query.fetchNextPage()
}
},
{ threshold: 1.0 }
)
observer.observe(loadMoreRef)
onCleanup(() => observer.disconnect())
})
return (
<div>
{/* Posts display */}
<div ref={loadMoreRef}>
<Show when={query.isFetchingNextPage}>
Loading...
</Show>
</div>
</div>
)
}
Refetch All Pages
import { createInfiniteQuery } from '@tanstack/solid-query'
function PostList() {
const query = createInfiniteQuery(() => ({
queryKey: ['posts'],
queryFn: fetchPosts,
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.nextCursor,
}))
const refreshAll = () => {
query.refetch() // Refetches all loaded pages
}
return (
<div>
<button onClick={refreshAll}>Refresh All</button>
{/* Posts display */}
</div>
)
}
With Loading States
import { createInfiniteQuery } from '@tanstack/solid-query'
import { Show, For, Switch, Match } from 'solid-js'
function PostList() {
const query = createInfiniteQuery(() => ({
queryKey: ['posts'],
queryFn: fetchPosts,
initialPageParam: 0,
getNextPageParam: (lastPage) => lastPage.nextCursor,
}))
return (
<Switch>
<Match when={query.isLoading}>
<div>Loading first page...</div>
</Match>
<Match when={query.isError}>
<div>Error: {query.error?.message}</div>
</Match>
<Match when={query.isSuccess}>
<For each={query.data.pages}>
{(page) => (
<For each={page.posts}>
{(post) => <div>{post.title}</div>}
</For>
)}
</For>
<Show when={query.hasNextPage}>
<button
onClick={() => query.fetchNextPage()}
disabled={query.isFetchingNextPage}
>
{query.isFetchingNextPage ? 'Loading...' : 'Load More'}
</button>
</Show>
</Match>
</Switch>
)
}
Page-Based Pagination
import { createInfiniteQuery } from '@tanstack/solid-query'
function PostList() {
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
},
}))
return <div>{/* ... */}</div>
}
Notes
The options parameter must be an accessor (function) to integrate with SolidJS reactivity.
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
- Solid Guide - Complete guide to Solid Query