useNexusInfiniteQuery
useNexusInfiniteQuery is a client-side hook for building “infinite scroll” or “load more” UIs. It manages page parameters, fetching, and state aggregation.
Import
import { useNexusInfiniteQuery } from 'next-nexus/client';Core Concepts
How It Works
The hook takes a definition factory function that accepts a page parameter (e.g., a cursor or page number). It calls this function sequentially to fetch pages. You must provide a getNextPageParam function that tells the hook how to get the parameter for the next page from the data of the last successfully fetched page.
Foreground vs. Background Revalidation
The revalidation logic for each page is identical to useNexusQuery. When fetching a new page (revalidateNext) or refetching an existing one (revalidatePage):
- Foreground: If the page has no cached entry, or if
keepStaleDataisfalseand the entry is stale. This contributes to theisPendingstate. - Background: If a stale entry exists and
keepStaleDataistrue. This contributes to theisPendingBackgroundstate.
State Flags and UX
isPending:trueduring any foreground fetch. Use this to show a primary loading state (e.g., a skeleton screen on initial load, or a spinner on a “Load More” button).isPendingBackground:trueduring any background fetch.hasNextPage:trueifgetNextPageParamreturned a value other thannullorundefinedfor the last page.
Signature
Parameters
getDefinition:(param) => NexusDefinition<TPage>— A factory function that accepts the current page parameter and returns aGETdefinition.options: An object to configure the hook. See Options below.
Return Value
An object containing:
data:{ pages: TPage[], pageParams: unknown[] } | undefinedheaders:Headers | undefined(from the most recent fetch)error:Error | nullisPending,isPendingBackground,isSuccess,isError:booleanhasNextPage:booleanrevalidateNext:() => Promise<void>(fetches the next page)prefetchRef?:React.Ref<HTMLDivElement>(an optional ref for a sentinel element to enable prefetching)
Options
initialPageParam: The parameter for the very first page (e.g.,0for page numbers, ornullfor cursor-based pagination).getNextPageParam:(lastPage: TPage, allPages: TPage[]) => param | null | undefined— A function that returns the parameter for the next page, ornull/undefinedto indicate the end.keepPages?:number- If set, keeps only the N most recent pages in memory to cap memory usage.revalidateOnMount?:boolean(default:true)revalidateOnWindowFocus?:boolean(default:false)keepStaleData?:boolean(default:true)prefetchNextOnNearViewport?:{ rootMargin?: string, threshold?: number }- Enables prefetching the next page when a sentinel element is near the viewport.
Example
1. Definition Factory
First, create a definition that accepts a page parameter, like a cursor.
// src/api/productDefinition.ts (excerpt)
export interface InfiniteProduct {
products: { id: string; name: string }[];
nextCursor?: string | null;
}
export const productDefinition = {
infiniteList: (cursor: string | null) =>
createApiDefinition<InfiniteProduct>({
method: "GET",
endpoint: cursor ? `/products?cursor=${cursor}` : "/products",
client: {
tags: ["products", `cursor:${cursor ?? "first"}`],
revalidate: 300,
},
}),
};2. Component Usage
Then, use the hook in your component.
"use client";
import { useNexusInfiniteQuery } from "next-nexus/client";
import { productDefinition } from "@/api/productDefinition";
export default function InfiniteProductList() {
const { data, hasNextPage, revalidateNext, prefetchRef, isPending } =
useNexusInfiniteQuery(productDefinition.infiniteList, {
initialPageParam: null,
getNextPageParam: (lastPage) => lastPage?.nextCursor ?? null,
prefetchNextOnNearViewport: { rootMargin: "200px" },
});
const allProducts = data?.pages.flatMap((page) => page.products) ?? [];
return (
<div>
<ul>
{allProducts.map((p) => (
<li key={p.id}>{p.name}</li>
))}
</ul>
<button
onClick={() => revalidateNext()}
disabled={!hasNextPage || isPending}
>
{isPending ? "Loading…" : "Load More"}
</button>
{/* Optional: Sentinel element for prefetching */}
<div ref={prefetchRef} style={{ height: 1 }} />
</div>
);
}See also