renderpx
Theme: auto

Stale-While-Revalidate

Show cached (possibly stale) data immediately, then revalidate in the background and update the UI when fresh data arrives.

The problem I keep seeing

Users navigate away and back, or switch tabs. If every visit waits for a fresh network response, the UI feels slow and flickers (empty → content). You want to show something immediately—ideally the last data you had—and then refresh in the background so the screen updates when new data is ready.

Naive approach

No cache: every mount triggers a fetch, and the UI shows loading until it completes. Revisits and tab switches always start from scratch.

tsx
Loading...

First improvement

Keep a simple cache (e.g. in a ref or module). On mount, if cache has data for this key, render it immediately and still fire a fetch; when the fetch completes, update cache and state. User sees stale data first, then fresh data without a loading block.

tsx
Loading...

Remaining issues

  • Cache invalidation: When to evict or refetch (e.g. after a mutation, or after a TTL). A library gives you staleTime and invalidation.
  • Multiple consumers: Several components may need the same query; a single cache (e.g. React Query) avoids duplicate requests and keeps them in sync.
  • Param changes: When the key (e.g. userId) changes, you may want to show the previous result as placeholder until the new one loads (no flash of empty).

Production pattern

Use React Query (or SWR). Set staleTime so data is considered “fresh” for a period and won’t refetch on remount; set gcTime so unused cache entries are kept for back navigation. You get SWR automatically: show cached data, refetch in background when stale. Optionally use placeholderData: (previous) => previous when the query key changes so the previous result stays visible until the new one loads.

tsx
Loading...
Placeholder data when key changestsx
Loading...

When I use this

  • Most GET data: Profile, settings, list views. Users expect instant display when they’ve seen it before; background refresh keeps it current.
  • Dashboard / metrics: Show last known values, refetch on interval or focus.
  • Skip when: Data must never be stale (e.g. critical financial step); then block UI until fresh data or show a clear “out of date” state.

Gotchas

  • staleTime vs gcTime: staleTime = “don’t refetch while fresh”; gcTime = “how long to keep unused data in cache.” Both affect when you see cached vs loading.
  • Refetch on focus: React Query refetches when the window regains focus by default. Turn off with refetchOnWindowFocus: false if you don’t want that.
  • Placeholder vs initialData: placeholderData is not cached and is used only for display; initialData is written into the cache and can prevent the first fetch if you’re not careful.

Cache Invalidation → · Loading States → · All patterns