useNexusQuery
useNexusQuery is a client-side hook for fetching, caching, and revalidating data defined by a definition. It’s designed for reading data in Client Components.
Import
import { useNexusQuery } from 'next-nexus/client';Signature
useNexusQuery<TData, TSelectedData = TData>(
definition: NexusDefinition<TData>,
options?: { ... }
): {
data: TSelectedData | undefined
headers: Headers | undefined
error: Error | null
isPending: boolean
isPendingBackground: boolean
isSuccess: boolean
isError: boolean
revalidate: () => Promise<void>
}definition: AGETdefinition object. The hook will throw an error if a non-GET definition is passed.options: An optional object to configure the hook’s behavior. See Options below.
Core Concepts
Hydration-Aware Fetching
useNexusQuery is aware of data fetched on the server and passed to the client via hydration. If data is available in the client cache upon mount, it will be rendered instantly without a new fetch, eliminating UI flickers.
Foreground vs. Background Revalidation
The hook automatically determines the revalidation mode when revalidate() is called or when automatic revalidation events (mount, window focus) occur:
- Foreground: If no cached entry exists, or if
keepStaleDataisfalseand the entry is stale. This setsisPendingtotrue. - Background: If a stale entry exists and
keepStaleDataistrue(the default). This setsisPendingBackgroundtotrue, whileisPendingremainsfalse. - No-op: If a fresh entry exists, no fetch is performed.
State Flags and UX
isPending:trueduring a foreground fetch. Use this to show a loading state like a spinner, especially when no data is yet available (isPending && !data).isPendingBackground:trueduring a background fetch. Use this to show a more subtle loading indicator (e.g., disabling a refresh button) while stale data remains visible.
Options
route?:string- Proxies the request through a Next.js Route Handler. When provided, the runtime clearsbaseURLfrom thedefinitionand uses therouteas the effective endpoint.enabled?:boolean(default:true) - If set tofalse, the hook will not fetch data or subscribe to cache updates.select?:(data: TData) => TSelectedData- A function to select or transform the data. The component will only re-render if the selected value changes.revalidateOnWindowFocus?:boolean(default:true) - Automatically revalidates when the window is refocused.revalidateOnMount?:boolean(default:true) - Automatically revalidates on mount if the data is stale.keepStaleData?:boolean(default:true) - Iftrue, stale data is kept on screen while a background revalidation occurs. Iffalse,databecomesundefinedduring a foreground revalidation.
Examples
Basic Usage
'use client';
import { useNexusQuery } from 'next-nexus/client';
import { productDefinition } from '@/api/productDefinition';
export function ProductListClient() {
const { data, isPending } = useNexusQuery(productDefinition.list);
if (isPending && !data) return <div>Loading...</div>;
const products = data ?? [];
return (
<ul>
{products.map(p => (
<li key={p.id}>{p.name}</li>
))}
</ul>
);
}Using the select Option
'use client';
import { useNexusQuery } from 'next-nexus/client';
import { statsDefinition } from '@/api/statsDefinition';
export function TotalCount() {
const { data: count } = useNexusQuery(statsDefinition.summary, {
select: s => s.total,
});
return <div>Total: {count ?? 0}</div>;
}Using a route Override
'use client';
import { useNexusQuery } from 'next-nexus/client';
import { productDefinition } from '@/api/productDefinition';
export function ProductListViaRoute() {
const { data } = useNexusQuery(productDefinition.list, { route: '/api/products' });
return <div>{data?.length ?? 0} items</div>;
}Reading Cached Headers
'use client';
import { useNexusQuery } from 'next-nexus/client';
import { listWithTotalDefinition } from '@/api/productDefinition'; // Assuming this exists
export function ProductListWithTotal() {
const { data, headers } = useNexusQuery(listWithTotalDefinition);
const total = headers?.get('x-total-count');
return (
<div>
Items: {data?.length ?? 0} / Total: {total ?? 'n/a'}
</div>
);
}See also