The Architecture Trap
I recently watched a senior dev spend three days trying to implement a robust infinite scroll using nothing but Next.js Server Components and Server Actions. By day three, they weren't coding; they were fighting the framework. They were wrestling with manual state synchronization, trying to patch together a global loading strategy, and attempting to 'reset' server-side data without blowing away the user's scroll position. It was a mess. This is the 'Server-First' trap: the seductive idea that because we can fetch data on the server, we no longer need sophisticated client-side state management.
Let’s be clear: React Server Components (RSC) are a generational leap forward for performance and SEO. But if you think they replace a client-side cache, you aren't building a web application; you're building a collection of documents. When we look at React Server Components vs TanStack Query, we shouldn't be looking for a winner. We should be looking for a marriage. Without TanStack Query, your modern React app is a house of cards waiting for a flaky 3G connection to knock it over.
The Illusion of 'Server-Only' Data Fetching
The marketing around Next.js 15 and React 19 suggests a world where 'fetch' is all you need. Next.js patches the global fetch API to provide server-side deduping and caching. On paper, it's brilliant. In practice, it's stateless. RSCs are great at the 'Initial Load' phase—getting those pixels on the screen fast for a great Largest Contentful Paint (LCP). But the moment a user clicks a 'Like' button, filters a list, or expects a background update, the RSC model starts to crack.
Server Components are fundamentally stateless. They cannot hold a mutation in progress, they cannot easily retry a failed request at the component level, and they certainly don't know how to handle 'stale-while-revalidate' logic on the client. As noted by Nir Tamir in 'The Limits of RSC', pure RSC architectures struggle significantly with patterns like infinite scrolling. To fetch page five of a list, a pure server-side approach often requires the server to re-execute the requests for pages one through four because the server doesn't 'remember' what it previously sent to the client.
Why Server Actions Aren't the Answer for Mutations
There is a common misconception that Server Actions vs react-query is a binary choice for mutations. Developers assume that because useActionState exists, useMutation is dead. This is a mistake. Server Actions provide the transport, but they don't provide the experience.
Think about 'Optimistic Updates.' When a user hits a 'Heart' icon, they expect an instant UI change. Implementing this with pure Server Actions requires manually managing a local state variable, handling the 'pending' transition, and manually rolling back that state if the server returns a 500 error. TanStack Query automates this entire lifecycle. It gives you a robust cache that can be updated optimistically and rolled back automatically. If you're building a high-interactivity 'dynamic island,' doing this manually is just asking for state-sync bugs.
The Power Duo: The Hybrid Pattern
The most successful teams I see aren't choosing one over the other; they are using a hybrid pattern. You use React Server Components vs TanStack Query as a collaborative stack. The server component fetches the initial data for SEO and speed, then 'dehydrates' that state into a HydrationBoundary. From that point on, TanStack Query takes the wheel on the client.
How it looks in practice:
- The Server: Fetches the 'Initial 20' items of a feed. This ensures the user sees content immediately without a loading spinner.
- The Hydration: That data is passed into the TanStack Query cache. The client now 'knows' about this data without having to fetch it again.
- The Interaction: When the user scrolls, TanStack Query handles the 'Fetch Next Page' logic, managing the loading states, retries, and cache persistence.
- The Sync: If the user tabs away and comes back, TanStack Query triggers a background refetch to ensure the data is fresh—something RSCs simply cannot do natively.
As argued in React Server Components + TanStack Query: The 2026 Data-Fetching Power Duo, this combination allows for 40-70% faster initial loads while maintaining the high-fidelity interactivity users expect from a modern SPA.
Complexity and the 'Double Cache' Critique
Critics often point out that this adds complexity. You have the Next.js server-side cache and the TanStack client-side cache. Isn't that doubling the cognitive load? Yes, it is. But this complexity is inherent to the problem of distributed state. Data on the server is a 'snapshot' of the past; data on the client is a 'projection' of the present.
Next.js's 'fetch' patching is often invisible and 'magical,' which makes it notoriously difficult to debug when a cache won't clear. TanStack Query is explicit. You have a Query Key. You have a stale time. You have a devtool that shows you exactly what is in the cache. In a production environment, I will take explicit complexity over 'magical' simplicity every single day.
The UX Gaps You Can't Ignore
If you abandon client-side caching, your app loses three critical pillars of UX:
- Window Focus Refetching: Users expect that when they return to a tab, the data refreshes. RSC-only apps stay stale until a hard refresh or a navigation event occurs.
- Polling and Background Sync: For dashboards or real-time feeds, you need automatic background updates. TanStack Query handles this with a single line of config.
- Granular Error Recovery: In a pure RSC app, a data failure often triggers the
error.tsxboundary, which usually replaces a large chunk of the page. With TanStack Query, you can fail gracefully within a tiny component and provide a localized 'Retry' button.
Closing the Gap
The debate over React Server Components vs TanStack Query is a false dichotomy. We are moving toward an era where the server handles the 'What' and the client handles the 'How.' RSCs are perfect for delivering the data shell, but TanStack Query is the engine that keeps that data alive, reactive, and resilient.
If you're starting a new Next.js 15 project, don't throw away your client-side tools in a fit of server-side maximalism. Use RSCs to win on performance metrics, but keep TanStack Query to win on user experience. Your users—and your sanity—will thank you.
What’s your strategy? Are you sticking to pure Server Actions for mutations, or are you still relying on the 'Query' way? Let's discuss on X or LinkedIn.


