From static to dynamic: using TanStack Query for real-time data in Next.js
How to layer client-side caching and background refresh on top of a static Next.js page with TanStack Query.
On this page
Static pages are fast, but some data changes while the user is looking at it. TanStack Query gives you caching, background refresh, and loading states without rewriting your routing model.
The mental model
Think in three states: fresh, stale, and background.
- Fresh: use the cache, do not refetch
- Stale: show the cache, refetch in the background
- Missing: show a loading state, fetch
You only configure how long something stays fresh.
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 60_000,
gcTime: 5 * 60_000,
retry: 2,
refetchOnWindowFocus: false,
},
},
});
A query with a clear key
The query key is the cache identity. Include everything the result depends on.
function useProjects(category: string) {
return useQuery({
queryKey: ["projects", category],
queryFn: async () => {
const res = await fetch(`/api/projects?category=${category}`);
if (!res.ok) throw new Error("Request failed");
return res.json();
},
});
}
Notice the res.ok check. fetch only rejects on network failure, never on a 500.
Mutations that update the cache
After a write, either invalidate the affected queries or update them directly.
const mutation = useMutation({
mutationFn: createProject,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["projects"] });
},
});
Keep the server boundary honest
Do not move everything to the client. Fetch what changes often on the client and keep the rest server-rendered.
| Data | Where |
|---|---|
| Marketing copy, metadata | Server |
| User-specific, frequently changing | Client, via TanStack Query |
| Initial page render payload | Server, then hydrate |
The best real-time experience is the one that still renders instantly on a slow connection.
Cache aggressively, refresh quietly, and show the last known value while you do it.
Continue reading
Related articles
Serverless file uploads with Supabase Storage and Next.js API routes
Secure, serverless file uploads in Next.js using Supabase Storage, with validation, size limits, and public access handling.
Building a modern todo app with Zustand, Firebase, and Next.js
A real-time todo app with authentication, CRUD operations, and predictable global state, built with Next.js, Firebase, and Zustand.
Integrating Razorpay payments in Next.js: a step-by-step guide
Set up checkout, verify signatures on the server, and handle webhooks safely when integrating Razorpay with Next.js.