NextJS: Prefetching/Hydration overwriting fresher cache data when using streaming SSR with staleTime
Hey folks,
tought i might also ask here on discord, here is the original discussion link:
https://github.com/TanStack/query/discussions/9559
I’ve run into something odd and couldn’t find an existing discussion about it (maybe it’s a bug, maybe I’m missing something 🤔).
Setup:
- Next.js 15+
- TanStack Query 5+
- Prefetching + streaming hydration as described here
- staleTime on the server side = 5 seconds (but issue happens with longer/shorter times too)
Scenario:
1. On the server, I prefetch /users with a staleTime of 5s.
2. On the client, I load the same query via useQuery and render a table of users.
Now, within that staleTime window the following needs to happen to experience my issue:
3. A user deletes a row → I send the request, optimistically update the UI with useMutation.
TanStack Query Devtools show the cache updated correctly, with “Last updated” set to now.
4. The user refreshes the page → since we’re still inside staleTime, the server doesn’t refetch, but hydrates old data from the initial prefetch, overwriting the newer client cache.
Devtools now show the “Last updated” time as the timestamp from the original page load.
Result: the deleted row reappears, even though it’s actually gone on the server.
What I’d expect:
When hydrating, the server-side data should be skipped if the client already has newer/fresher data in the cache.
Question:
Is this expected behavior? Am I missing a setting here? Or could this be a Next.js SSR quirk?
GitHub
NextJS: Prefetching/Hydration overwriting fresher cache data when u...
Hey folks, I’ve run into something odd and couldn’t find an existing discussion about it (maybe it’s a bug, maybe I’m missing something 🤔). Setup: Next.js 15+ TanStack Query 5+ Prefetching + stream...
14 Replies
like-gold•2w ago
since we’re still inside staleTime, the server doesn’t refetchif you refresh the page, the server will run. there is no caching on the server in react query you'd probably need to customize nextJs's caching to not re-run the server (I think they also have a staleTime setting) or do some caching of the query-client on the server
multiple-amethystOP•2w ago
I did the following:
1. Setting expirimental "staleTime" (next.config.mjs) for dynamic and static to "0"
2. While also calling "router.refresh" after the deletion of the row.
The error still persists, tanstack query still hydrates the old data received from the server (im not sure why it receives that data and what kinda cache is doing this since i now basicly disabled the nextjs cache and forcefully refresh it.)
I can also confirm that the page reload itself is happening (i forcefully refresh in my browser) and a console.log on the backend shows that i am reaching the new queryClient.prefetchQuery call. However queryFn for whatever strange reason is not called (i console.log inside ther), it is only called after the 5 second staleTime (of the backend prefetch request) are over. (With no cache on the backend, why has it a staleTime setting, and how does it know when it is reached?).
It btw. is not my frontend re-fetching it after the 5 seconds because my frontend query has a staleTime of Number.Infinite
multiple-amethystOP•2w ago
I made a small repo which contains code to reproduce it:
https://github.com/toitzi/react-query-cache-issue
GitHub
GitHub - toitzi/react-query-cache-issue: Repo to reproduce my cache...
Repo to reproduce my cache issue (Stale data re-appears) - toitzi/react-query-cache-issue
like-gold•2w ago
you create a
const queryClient = new QueryClient()
outside of your component, on the server, which means it just lives there and is shared between all requests. The docs explicitly mention to not do that
https://github.com/toitzi/react-query-cache-issue/blob/1cdeab6592e3add6cee69d96be6d1ffc6e72c8e5/app/page.js#L6
that's why we have this makeQueryClient()
function that always creates a new client when on the serverlike-gold•2w ago
Server Rendering & Hydration | TanStack Query React Docs
In this guide you'll learn how to use React Query with server rendering. See the guide on for some background. You might also want to check out the before that. For advanced server rendering patterns,...
multiple-amethystOP•2w ago
This page is server side you sent, and not my provider, in my provider i have: it correctly and according to the docs:
https://github.com/toitzi/react-query-cache-issue/blob/1cdeab6592e3add6cee69d96be6d1ffc6e72c8e5/lib/providers/query-client-provider.jsx
The snippet you shared it from the server page, looking at the documentation for ssr:
https://tanstack.com/query/v5/docs/framework/react/guides/advanced-ssr
this is the correct way to do it on the server side page
GitHub
react-query-cache-issue/lib/providers/query-client-provider.jsx at ...
Repo to reproduce my cache issue (Stale data re-appears) - toitzi/react-query-cache-issue
Advanced Server Rendering | TanStack Query React Docs
Welcome to the Advanced Server Rendering guide, where you will learn all about using React Query with streaming, Server Components and the Next.js app router. You might want to read the before this on...
multiple-amethystOP•2w ago
The difference is in the docs it is done inside the component and i have it outside
putting it inside the server component, makes seemingly the prefetch not work at all (i updated my repo, if you move the query client call inside the server component, the frontend executes/waits for the frontend request instead of hydrating the prefetched data, i added an artificial delay so this is noticable)
like-gold•2w ago
every place in the SSR docs creates a
new QueryClient()
inside the server component before calling prefetchQuery
:
- https://tanstack.com/query/v5/docs/framework/react/guides/advanced-ssr#prefetching-and-dehydrating-data
just search for prefetchQuery
, it's always:
Advanced Server Rendering | TanStack Query React Docs
Welcome to the Advanced Server Rendering guide, where you will learn all about using React Query with streaming, Server Components and the Next.js app router. You might want to read the before this on...
like-gold•2w ago
whenever you create the
QueryClient
in the module scope, it will live on after a request on the server. Which is why we document that this shouldn't be done.
so yes, the idea is: there is no caching on the server between requests. When you call prefetchQuery
, the fetch will happen. always. staleTime on prefetchQuery
doesn't help.
it's like doing const data = await fetch(...)
in a server component. This will always happen when the server component "runs". The idea is to not make the server component run, with next's staleTime or static or prerendering or whatever else they offer to not run server componentsmultiple-amethystOP•2w ago
okay thank you very much i understand that part now,
but if i move it inside the component like you shared, data is not prefetched / hydrated instantly? It will still use the fetch from the frontend and wait for that data?
like-gold•2w ago
no that should work:
- you prefetch on the server
- the
HydrationBoundary
sends it to the client
- the client picks it up and puts it into the QueryCache on the client
- if you have staleTime on the client, no fetch from the client should happenmultiple-amethystOP•2w ago
1. Yes that is what i expected, i updated my repository, in my prefetch function i load the data instantly (few ms) and dehydrate
2. In my frontend i use an action with an aritificial 2 seconds delay
If i load the page now no prefetched data is visible, it will fetch via the action from the frontend and wait those 2 seconds
like-gold•2w ago
your issue with the current code, is, again, documented here:
https://tanstack.com/query/v5/docs/framework/react/guides/advanced-ssr#streaming-with-server-components
you prefetch without
await
,which means we stream the promise to the client. In that case you need useSuspensQuery
, not useQuery.
Note that you could also useQuery instead of useSuspenseQuery, and the Promise would still be picked up correctly. However, NextJs won't suspend in that case and the component will render in the pending status, which also opts out of server rendering the content.so you either need to
await prefetchQuery(..)
or switch to using suspenseAdvanced Server Rendering | TanStack Query React Docs
Welcome to the Advanced Server Rendering guide, where you will learn all about using React Query with streaming, Server Components and the Next.js app router. You might want to read the before this on...
multiple-amethystOP•2w ago
strangly enough, using suspense query and a suspense boundary will still wait to display content until the client query is done, it does not show me the prefetched hydrated state. I can use the expirmental ReactQueryStreamedHydration and omit the prefetch, the queryFn then seems to correctly start its run on the server and work as i would expect, i think thats okay for my case but do you have a guess why the first case is not working?
Thanks a lot btw for your help
(it works in the first case if i await the prefetch, but that should not be necessary afaik)
ah nevermind i figured my problem, thank you very much for all the help! 🙂