public-rules › TanStack Query techs/tanstack-query

Set staleTime from how fast data changes

MEDIUM1.0.0
When to apply Before planning, writing, changing, or reviewing TanStack Query client defaults or per-query staleTime or gcTime, or diagnosing refetches on every mount, focus, or navigation.

Set a client-wide default staleTime above zero, and override it per query based on how quickly that data changes and how much outdated data would matter. Leave gcTime at its default unless you have a specific reason to change it.

Implementation

  • Set defaultOptions.queries.staleTime on the QueryClient, such as one minute, rather than relying on the default of zero.
  • Give slowly changing data, such as reference lists or configuration, a longer staleTime, up to Infinity with explicit invalidation.
  • Give fast-changing data a short staleTime, or refetch on an interval when users need it live.
  • Put per-query values in the query's queryOptions factory so every use gets the same freshness.
  • gcTime controls how long unused data stays in memory, five minutes by default in the browser. Raise it when users often return to the same data after longer gaps, or when persisting the cache. Lower it for large results viewed once. Avoid gcTime: 0 for queries rendered on the server.
  • Treat suggested durations as starting points; the right value depends on how stale data can be before users are misled.

Rationale

Data is fresh for staleTime after it is fetched, and TanStack Query does not refetch fresh data when a component mounts or the window regains focus. With the default of zero, every new observer and every focus triggers a request, which multiplies load for data that rarely changes. gcTime is independent: it decides when data no longer used by any component is removed from the cache.

Examples

Incorrect (counterexample):

const queryClient = new QueryClient();

useQuery({ queryKey: ['categories'], queryFn: fetchCategories });

Categories rarely change, but they are refetched every time a component using them mounts and every time the window regains focus.

Correct:

const queryClient = new QueryClient({
  defaultOptions: { queries: { staleTime: 60 * 1000 } },
});

export const categoryQueries = {
  all: () =>
    queryOptions({
      queryKey: ['categories'],
      queryFn: fetchCategories,
      staleTime: 30 * 60 * 1000,
    }),
};

Validation

Watch the network panel while navigating between screens and refocusing the window, and check that slowly changing data is not refetched each time. Check that the QueryClient sets a default staleTime.

A staleTime of zero is not a violation for data that must always be refetched when shown.