React Query: What Is `staleTime` and Why Should You Care?
Source: Dev.to
What Does “Stale” Mean?
Think of it like milk in your fridge.
- Fresh milk – you just bought it, you trust it, you drink it without thinking.
- Stale milk – it’s been sitting there for a while; it might still be fine, but you’d probably want to check or get a new one.
React Query treats your fetched data the same way:
- Fresh data – “I just got this, no need to fetch again.”
- Stale data – “This might be outdated. I’ll refetch it when I get the chance.”
Important: Stale data is still shown to the user from the cache. React Query doesn’t show a loading spinner—it silently refetches in the background and updates the UI only if something changed.
The Default Behavior (staleTime = 0)
By default, staleTime is 0. That means the instant your data is cached, React Query marks it as stale.
So even if you fetched a list of books 1 second ago, React Query thinks: “This could be outdated, let me refetch it.”
When Does React Query Actually Refetch Stale Data?
Refetches only occur on specific triggers:
| Trigger | What happens |
|---|---|
| Window refocus | You switch to another tab, then come back |
| Component mount | A component using this query mounts/remounts |
| Network reconnect | You go offline and come back online |
Common Misconception
“Does moving my mouse trigger a refetch?”
Nope! Only switching away from the tab and coming back (window refocus) does. If you stay on the same tab for 30 minutes, no refetch happens until one of the triggers above occurs.
Setting staleTime: A Real Example
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 1000 * 60 * 5, // 5 minutes
},
},
});
function App() {
return (
<QueryClientProvider client={queryClient}>
{/* Your app components */}
</QueryClientProvider>
);
}
With this setup:
- User visits the page → React Query fetches books from the API.
- User switches tabs and returns 2 minutes later → Data is still fresh. No refetch; result comes instantly from cache.
- User returns 7 minutes later → Data is now stale. React Query refetches in the background, and the UI updates if new data is available.
staleTime vs cacheTime (gcTime) — Don’t Mix Them Up!
| staleTime | gcTime (formerly cacheTime) | |
|---|---|---|
| What it controls | How long data is considered fresh | How long unused cached data stays in memory |
| Default | 0 (immediately stale) | 5 minutes |
| After it expires | Data is marked stale; refetched on next trigger | Data is garbage‑collected (removed from cache) |
Think of it this way:
staleTime= “How long should I trust this data?”gcTime= “How long should I keep this data in memory after no component is using it?”
Quick Visual Timeline
Fetch happens at 0:00
|
|-- 0:00 to 5:00 → Data is FRESH (no refetch, served from cache)
|-- 5:00+ → Data is STALE (refetch on next trigger)
|
|-- If no component uses this data for 5 min → cache is GARBAGE COLLECTED
Per-Query staleTime
You can also set staleTime on individual queries instead of globally:
// This specific query stays fresh for 10 minutes
const { data } = useQuery({
queryKey: ["books", id],
queryFn: () => fetchBook(id),
staleTime: 1000 * 60 * 10, // 10 minutes
});
This overrides the global default for just this one query.
TL;DR
staleTime = 0(default): data is immediately stale, refetched on every trigger.staleTime = 1000 * 60 * 5: data is fresh for 5 minutes, avoiding unnecessary API calls during that period.- Stale data is still displayed from cache — refetch happens silently in the background.
- Refetch triggers: window refocus, component mount, network reconnect — not mouse movement.
staleTime(freshness) andgcTime(cache lifetime) are distinct concepts.
Happy coding!