The Short Answer
No, the tRPC adapter should not—and does not—automatically trigger background refetches simply because a query has become stale. In the tRPC and TanStack Query architecture, staleTime defines when data is considered old, but it does not act as a trigger for a network request. A refetch only occurs when a specific trigger event happens while the data is in a stale state.
Understanding the Trigger Mechanism
Because tRPC delegates caching and synchronization to TanStack Query, it follows the "stale-while-revalidate" pattern. It is important to distinguish between the state of the data and the action of fetching it.
Confirmed Behavior
- Stale State: When
staleTime expires, the query is marked as stale. No network request is sent at this exact moment.
- Refetch Triggers: A background refetch is only initiated if the query is stale AND one of the following occurs:
- The component mounting the query re-renders or re-mounts.
- The browser window regains focus (
refetchOnWindowFocus).
- The network reconnects (
refetchOnReconnect).
- A manual invalidation is called via
queryClient.invalidateQueries().
Likely Explanation for "Missing" Updates
If your UI feels inconsistent because data isn't updating automatically, it is likely because you are relying on staleTime to push updates to the client. TanStack Query is designed to be pull-based. If you require the server to push updates or the client to check for changes regardless of user interaction, you are looking for polling or subscriptions, not stale-time management.
Implementation Steps for Automatic Freshness
Depending on your version (tRPC v10+ using TanStack Query v4/v5), use these scoped configurations in your query hooks:
Option 1: Polling (Fixed Intervals)
Use refetchInterval to force a background refetch every X milliseconds, regardless of window focus.
const query = trpc.user.getProfile.useQuery(undefined, {
refetchInterval: 30000, // Refetch every 30 seconds
staleTime: 20000, // Consider data fresh for 20 seconds
});
Option 2: Event-Driven Invalidation
Instead of automatic timers, trigger refetches immediately after a mutation to ensure the UI is consistent.
const mutation = trpc.user.updateProfile.useMutation({
onSuccess: () => {
utils.user.getProfile.invalidate();
},
});
Verification
To verify your current behavior, open the Browser DevTools Network tab and observe the requests. If requests only fire when you click away from the tab and back, refetchOnWindowFocus is your primary trigger. If no requests fire until a page refresh, your staleTime may be set too high or your triggers are disabled.
Diagnostic Detail Needed: Are you using tRPC Subscriptions (WebSockets)? If so, the recommendation shifts from polling to event-based state synchronization.