Skip to content

raf-query is early. The API can still change between minor releases, so use it at your own risk.

raf-query

Guides

Important defaults

When raf-query fetches, refetches, retries and garbage-collects by default, and which options change it.

Out of the box, raf-query is configured with aggressive but sane defaults, the same ones TanStack Query uses. They can catch you off guard if you don't know them, so keep them in mind as you read the rest of the guides:

  • Cached data is stale immediately (staleTime: 0).

  • Stale data refetches in the background when:

    • a loader or server component starts the query again (prefetch, ensure or fetch), which TanStack Router does on every navigation
    • the window becomes visible again (window focus refetching), for queries being read
    • the browser comes back online, for queries being read
    • the query is invalidated
  • Mounting a component never fetches. Unlike TanStack Query, raf-query has no refetch on mount: whatever started the query decides when it loads. Rendering the same handle in ten components reads one cache entry.

  • Polling is per read. useQuery(factory, handle, { refetchInterval }) refetches on a timer while that component is mounted and the page is visible, independent of staleTime. See Polling.

  • Unread entries are kept for five minutes (gcTime: 300_000). An entry nobody reads is inactive: it stays in the cache in case it's needed again, then is garbage-collected.

  • Failed requests retry three times, with a delay that doubles from one second, up to 30 seconds, before the error reaches the UI. Retries wait while the browser is offline. See Retries.

  • Data is structurally shared. When a refetch returns data that's deeply equal to what's cached, the cache keeps the old objects, so unchanged parts keep their identity and nothing re-renders. See Render optimizations.

On the server

A server client (one per request) renders once, so it behaves differently:

  • Cached data is always reused, whatever staleTime says. A background refetch on the server could only repeat a request whose result is never shown. Entries marked by invalidate still refetch.
  • No retries (retry: 0) and no garbage collection (gcTime: Infinity): the cache lives as long as the request.
  • useIsFetching() returns 0, so loading indicators never appear in server HTML.

Changing the defaults

Every option can be set per query in the factory, or for every query with config.defaults. A query's own option wins:

const const queryClient: QueryClient<never>queryClient = new new QueryClient<never>(config?: QueryClientConfig<never>): QueryClient<never>QueryClient({
	Config.defaults?: QueryBehavior | undefineddefaults: {
		QueryBehavior.staleTime?: number | undefinedstaleTime: 30_000,
		QueryBehavior.gcTime?: number | undefinedgcTime: 10 * 60_000,
		QueryBehavior.retry?: RetryPolicy | undefinedretry: 1,
		QueryBehavior.refetchOnWindowFocus?: boolean | undefinedrefetchOnWindowFocus: false,
	},
});
OptionDefault
staleTime0 ms
gcTime5 minutes; Infinity on the server
retry (count, boolean, predicate, Schedule)3; 0 on the server
retryDelay (ms, or a function of the count)Doubling from 1 s, at most 30 s
refetchOnWindowFocus, refetchOnReconnecttrue (only read, stale queries refetch)
structuralSharingtrue
enabledtrue; false disables it, such as on the server
metaNone

Watch the defaults at work in Caching, which has a live demo of an entry going from fresh to stale to garbage-collected.