Replies: 3 comments 3 replies
-
that depends what
usually, you’d want to always create a new QueryClient on the server within the sever component to avoid leaking things between requests. This is the page you’d want to read: https://tanstack.com/query/latest/docs/framework/react/guides/advanced-ssr |
Beta Was this translation helpful? Give feedback.
-
My exact import { convertToMilliseconds } from '@/lib/utils/convert-to-milliseconds';
import { QueryClient } from '@tanstack/react-query';
function makeQueryClient() {
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: convertToMilliseconds(5, 'minutes'),
retry: 1,
},
},
});
return queryClient;
}
let browserQueryClient: QueryClient | undefined = undefined;
export function getQueryClient() {
if (typeof window === 'undefined') {
// Server: always make a new query client
return makeQueryClient();
}
// Browser: make a new query client if we don't already have one
// This is very important, so we don't re-make a new client if React
// suspends during the initial render. This may not be needed if we
// have a suspense boundary BELOW the creation of the query client
if (!browserQueryClient) browserQueryClient = makeQueryClient();
return browserQueryClient;
} I always call this method, both server-side and client-side (also I don't use |
Beta Was this translation helpful? Give feedback.
-
To that extend, I`ve recently watched the server-side rendering from the course. I will just paste the comment here so it is publicaly available: I'm using the exact setup from this section of the React Query docs: The problem I'm running into is that I'm always getting stale data, even after a hard refresh ( App flow:
But if I do this in the server component: import { getQueryClient } from '../../get-query-client';
export const dynamic = 'force-dynamic';
const queryClient = getQueryClient();
export default async function RootLayout({ children }: NextLayoutProps) {
await queryClient.invalidateQueries(booksQuery()); // <- THIS FIXES IT
await queryClient.prefetchQuery({ ...booksQuery(), queryFn: fetchBooks });
return (
<HydrationBoundary state={dehydrate(queryClient)}>
{children}
</HydrationBoundary>
);
} …it works. Without the invalidation, What I’m suspectingThis might be related to Next.js 15’s caching. I’m already using It’s strange, because the mutation does trigger a client-side invalidation, but it doesn't seem to affect the server-rendered hydration when navigating back. I’m testing a few other patterns for initializing the query client (based on the docs), but figured I’d ask in case someone else has run into this. Does Note: In both client and server components, when I want to access the query client, I just do the following (pretty sure I saw this pattern in the docs): "use client"
import { getQueryClient } from '@/app/get-query-client';
const queryClient = getQueryClient();
export default function MyClientComponent() {
...
} import { getQueryClient } from '@/app/get-query-client';
const queryClient = getQueryClient();
export default async function MyServerComponent() {
...
} Extra context: const queryClient = getQueryClient();
export default function Providers({ children }: ProvidersProps) {
return (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
);
} I came across a few GitHub issues and videos that mention wrapping it in |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
First of all, thanks for the amazing work on TanStack Query and its amazing course. I’ve been using React Query for a while, but after diving deeper I’m facing some edge cases that are probably a skill issue on my side, and I’d love some insight.
📖 Context
I’m using Next.js 15 (App Router) with both server-side prefetching and client-side React Query hydration. My app structure is something like this:
In
RootLayout.tsx
:In
Providers.tsx
:🤔 The Problem
After some debugging, I noticed:
queryClient
instances.My suspicion is that I’m creating a
queryClient
outside of theProviders
component (server side), and then again creating another one (now client side insideProviders
). Then I hydrate the state from the server-prefetched queries with the first instance — but the client uses a different one. Could this be causing the inconsistent behavior I’m seeing (some queries stale, others up-to-date)?🔄 Prefetching Specifics
Inside inner page layouts (like
/product
or/product/[id]
), I also prefetch page-specific queries server-side.But sometimes — seemingly random — after mutating a product, navigating back to the list or product page might still show stale data.
Notes:
📌 Questions
Is my RootLayout + Providers setup flawed?
Am I mishandling queryClient instancing and scoping, causing server and client to work with different clients?
Extra question: Should I await
queryClient.invalidateQueries()
if I need to trigger a router navigation right after?(Or is it safe to navigate and rely on background refetching?)
Would appreciate any guidance on properly handling queryClient instances between server-side prefetch and client-side hydration in Next.js App Router.
Thanks a lot!
After notes:
If I'm asking here, it's because I’ve spent quite some time researching, testing, and debugging this on my own. And in all my skill issue ignorance, I haven’t been able to find a proper fix or a clear explanation for this specific scenario.
So if your first thought is "look at the docs", I’d genuinely appreciate it if you could drop a link to the exact section or topic you have in mind — it would be super helpful. 🙏
Beta Was this translation helpful? Give feedback.
All reactions