For every query, define a clear staleness/refetch policy—especially for SSR/hydration—rather than relying on defaults or assumptions.
Standards
staleTime deliberately (default is 0, which means cached data is treated as stale).staleTime: Infinity vs staleTime: 'static':
Infinity, “always” refetch triggers can still fire.'static', those “always” triggers won’t revive the query; you must explicitly reset/clear via the query client.HydrationBoundary prevents unnecessary refetching during hydration; hydrated queries will only refetch on mount if refetchOnMount: 'always' is explicitly set.
prefetchQuery uses the same freshness config as your useQuery observers. If you didn’t provide a staleTime/defaults, the call may treat cached data as stale (default 0).gcTime: 0 as it can cause hydration errors; be intentional about cache retention.Example (SSR + explicit stale/refetch intent)
// query defaults (e.g., in your QueryClient)
const queryClient = new QueryClient({
defaultOptions: {
queries: {
// pick one intentionally
staleTime: 'static', // or Infinity, or a number
// for hydration: only force mount refetch when truly needed
refetchOnMount: 'always',
// error recovery should still happen (ensure your retry policy allows it)
retry: 2,
},
},
})
// SSR/CSR: render via HydrationBoundary to prevent unnecessary hydration refetch
return (
<HydrationBoundary state={dehydratedState}>
<App />
</HydrationBoundary>
)
Practical checklist for PRs
staleTime (number / Infinity / 'static')?refetchOnMount), and why?