Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/get-convex/convex-react-query/llms.txt

Use this file to discover all available pages before exploring further.

staleTime: Infinity

This is the most significant behavioral difference. With regular fetch-based queries, TanStack Query marks data as stale after staleTime elapses and schedules a background refetch. With convexQuery, the Convex server pushes updates proactively over WebSocket — the client never needs to ask for fresh data. staleTime: Infinity is set automatically by convexQuery() and convexAction(), so TanStack Query will never trigger a background refetch for these queries.
// fetch-based: data goes stale and triggers background refetches
useQuery({
  queryKey: ["messages"],
  queryFn: () => fetch("/api/messages").then(r => r.json()),
  staleTime: 30_000, // refetch after 30s
});

// Convex: data is never stale — pushed from the server
useQuery(convexQuery(api.messages.list, {}));
// staleTime: Infinity is set automatically

No invalidation needed

With fetch-based mutations, you must call queryClient.invalidateQueries() after a successful mutation to prompt TanStack Query to refetch the affected data. With Convex, the backend pushes updated query results automatically after any mutation that changes the underlying data.
// fetch-based mutation: must manually invalidate
const { mutate } = useMutation({
  mutationFn: (body) => fetch("/api/messages", { method: "POST", body }),
  onSuccess: () => queryClient.invalidateQueries({ queryKey: ["messages"] }),
});

// Convex mutation: no invalidation needed
const { mutate } = useMutation({
  mutationFn: useConvexMutation(api.messages.send),
  // No onSuccess needed — updated results are pushed automatically
});
Calling queryClient.invalidateQueries() on a convexQuery is harmless but unnecessary. The cache will be updated by the Convex subscription before any refetch triggered by invalidation could complete.

gcTime controls subscription duration

With fetch-based queries, gcTime only determines how long an unused cache entry stays in memory before being garbage-collected. With Convex queries, gcTime also controls how long the WebSocket subscription stays active after all observers unmount. While the subscription is alive (even with no mounted observers):
  • The cached value continues to receive pushed updates from the server.
  • Remounting a component that uses this query is instant — the fresh cached value is available immediately, with no loading state.
Set gcTime to at least a few seconds for most queries. A value of 10_000 (10 seconds) is a reasonable default for components that mount and unmount regularly.
useQuery({
  ...convexQuery(api.messages.list, {}),
  gcTime: 10_000, // subscription stays active for 10s after last observer unmounts
});
Once gcTime expires, ConvexQueryClient calls unsubscribe() on the Convex Watch and the cache entry is removed.

No effect from refetchOnWindowFocus / refetchOnReconnect

TanStack Query’s refetchOnWindowFocus and refetchOnReconnect options trigger background fetches when the browser window regains focus or the network reconnects. These behaviors have no meaningful effect on reactive Convex queries because the Convex WebSocket connection handles reconnection and result freshness automatically. You can safely leave these options at their defaults or disable them — either way they are harmless for convexQuery.
On reconnect, the Convex client re-establishes the WebSocket connection and re-runs all active subscriptions. Any changes that occurred while disconnected are pushed as part of the reconnect flow.

Actions vs. queries

convexAction does not receive pushed updates. Actions run imperatively and return a single result; they are not subscriptions. Actions behave like standard fetch-based queries:
  • They run once on mount.
  • They refetch based on standard TanStack Query rules (refetchOnWindowFocus, refetchOnReconnect, staleTime).
  • invalidateQueries can be used to trigger a manual refetch.
// Reactive — pushed updates from server
useQuery(convexQuery(api.messages.list, {}));

// Not reactive — fetches once, then follows standard refetch rules
useQuery(convexAction(api.weather.getSFWeather));
staleTime: Infinity is also set on convexAction by default. If you want the action to refetch periodically, override staleTime explicitly:
useQuery({
  ...convexAction(api.weather.getSFWeather),
  staleTime: 60_000, // refetch after 60s
});

Comparison table

convexQueryconvexActionfetch-based
UpdatesPushed from serverPoll / refetchPoll / refetch
staleTimeAlways InfinityInfinity by default (configurable)Configurable
invalidateQueries neededNoSometimesYes
SSR supportYes (HTTP)Yes (HTTP)Yes