šŸš€ LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
šŸŽ“ COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.
HTML MASTER CLASS /// LEARN TAGS /// BUILD STRUCTURE /// SEMANTIC WEB /// HTML MASTER CLASS /// LEARN TAGS ///

TanStack Query Caching: staleTime, gcTime, and Invalidation

Understand TanStack Query's caching model: staleTime, gcTime, and precise cache invalidation with hierarchical query keys.

⚔ Total XP: 0|šŸ’» react XP: 0

Skill Matrix

UNLOCK NODES BY LEARNING NEW TAGS.

System Hub

Caching fundamentals.

Quick Quiz //

What does staleTime control?


šŸš€ LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
šŸŽ“ COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.

TanStack Query's cache answers two independent questions about any data: is it stale, and is it still cached at all? This lesson covers staleTime, gcTime, and how to manually force a refetch with invalidateQueries using hierarchical query keys.

1Fresh vs. Stale: Two Different Questions

TanStack Query's cache separately tracks whether data is stale — should it be refetched given the opportunity — and whether it's still present in the cache at all, since unused data eventually gets garbage collected. Treating these as two independent settings is essential to configuring caching correctly.

2staleTime: When Cached Data Needs Refreshing

staleTime defaults to 0, meaning data is considered stale immediately after fetching, causing aggressive refetching on triggers like mount or window focus. Setting a longer staleTime tells the library the data can be trusted as fresh for that duration, skipping refetch triggers during that window.

3gcTime: When Unused Data Is Discarded

gcTime controls how long data with no currently-subscribed component (inactive data) remains in memory before being fully removed from the cache, defaulting to 5 minutes. This is why navigating back to a recently viewed page can show cached data instantly, without waiting on a fresh network request.

4Manual Invalidation with invalidateQueries

When application code knows data has changed — such as right after a successful save — calling queryClient.invalidateQueries marks matching queries as stale and triggers an immediate refetch for any currently active ones, regardless of the configured staleTime.

5Precise Invalidation with Query Key Matching

invalidateQueries matches query keys by prefix rather than exact equality, so invalidating ['user'] affects every query starting with that prefix, like ['user', 1] and ['user', 2]. Structuring query keys hierarchically, from general to specific, gives precise control over exactly what gets invalidated.

6Step-by-Step Breakdown

Fresh vs. Stale: Two Different Questions. TanStack Query's cache answers two separate questions about any piece of data: is it stale (should it be refetched given the chance?) and is it still in the cache at all (garbage collected or not?). Understanding these as two independent settings is the key to configuring caching correctly.

staleTime: When Cached Data Needs Refreshing. By default, staleTime is 0 — data is considered stale the instant it's fetched, so TanStack Query refetches aggressively on every trigger (mount, focus, reconnect). Setting staleTime: 60000 tells it 'this data is good for 60 seconds; don't bother refetching it during that window even if a trigger fires.'

What does staleTime: 60000 tell TanStack Query about a query's data?

  • →The data is considered fresh for 60 seconds and won't be refetched during that window
  • →The data is deleted from the cache after 60 seconds

gcTime: When Unused Data Is Discarded. gcTime (garbage collection time, default 5 minutes) controls how long INACTIVE cached data — data with no component currently subscribed to it — stays in memory before being fully removed. This is why navigating back to a recently-viewed page often shows data instantly, even without a fresh network request.

Manual Invalidation with invalidateQueries. Sometimes you know data changed — say, right after a successful save — and want to force a refetch immediately, regardless of staleTime. Calling queryClient.invalidateQueries({ queryKey: [...] }) marks matching queries as stale and triggers an immediate refetch for any of them currently in use.

After a user successfully updates their profile, what's the correct way to make the UI reflect the new data immediately?

  • →Call queryClient.invalidateQueries() to force a fresh refetch
  • →Reload the entire page with window.location.reload()

Precise Invalidation with Query Key Matching. invalidateQueries matches queryKeys by prefix, not exact equality — queryKey: ['user'] invalidates every query starting with 'user', including ['user', 1] and ['user', 2]. Structuring queryKeys hierarchically, from general to specific, gives you precise control over exactly what gets invalidated.

Mastery Achieved. You now understand TanStack Query's caching model: staleTime for refetch aggressiveness, gcTime for how long unused data survives in memory, and invalidateQueries for forcing an immediate refetch after a known data change, using hierarchical query keys for precise control. Next, you'll learn Mutations for writing data back to the server.

Level Up šŸš€

Advanced cheat sheets, SEO tricks, and interview prep for this topic.

Browser Support

ChromeSupported

Fully supported.

FirefoxSupported

Fully supported.

SafariSupported

Fully supported.

EdgeSupported

Fully supported.

Accessibility (A11y)

1Cache-Served Instant Data Still Needs a Refresh Indicator If Refetching

When TanStack Query shows cached data instantly while silently refetching in the background (isFetching without isLoading), consider a subtle, non-intrusive indicator so users relying on assistive technology understand data may still be updating.

SEO Implications

  • 1

    Caching Configuration Doesn't Affect Server-Rendered Content

    staleTime and gcTime govern client-side cache behavior after hydration; they have no bearing on what's present in server-rendered HTML seen by crawlers.

Best Practices

Set staleTime Based on How Often the Data Actually Changes

Data that rarely changes (like a list of countries) can have a long staleTime to avoid unnecessary refetches; frequently changing data (like a live order status) should keep a short or zero staleTime.

Invalidate the Narrowest Query Key That's Actually Affected

Prefer invalidating ['user', userId] over the broader ['user'] when only one user's data changed, to avoid unnecessarily refetching every cached user query.

Frequent Bugs

THE BUG

After successfully saving an edit, the UI still shows the old, stale data.

THE FIX

The mutation succeeded, but nothing told TanStack Query the related query is now stale. Call queryClient.invalidateQueries with the affected queryKey after the mutation resolves to force a refetch.

THE BUG

A rarely-changing dataset refetches far more often than expected, causing unnecessary network load.

THE FIX

The query is likely using the default staleTime of 0, causing a refetch on every mount or window focus. Set an appropriate staleTime matching how often the underlying data actually changes.

Real-World Examples

Invalidating a User's Query After a Profile Update

A settings form updates a user's profile via a mutation. After the mutation succeeds, calling queryClient.invalidateQueries({ queryKey: ['user', userId] }) ensures every component displaying that user's data — the header avatar, the settings page itself — refetches and shows the updated information immediately.

async function handleSave(updatedProfile) {
  await saveProfile(updatedProfile);
  queryClient.invalidateQueries({ queryKey: ['user', userId] });
}

Interview Prep

?Frequently Asked Questions

Pascual Vila

Pascual Vila

Frontend Instructor // Code Syllabus

Common Pitfalls & Errors

The Error //

Forgetting to invalidate related queries after a successful mutation

await updateUser(userId, changes); queryClient.invalidateQueries({ queryKey: ['user', userId] });

The Solution //

TanStack Query has no way to know a mutation changed server data unless told explicitly. Call queryClient.invalidateQueries with the affected queryKey right after the mutation succeeds.

The Error //

Invalidating an overly broad queryKey, causing unnecessary refetches across unrelated data

// Too broad: refetches every user, even unaffected ones queryClient.invalidateQueries({ queryKey: ['user'] }); // Precise: only refetches this one user queryClient.invalidateQueries({ queryKey: ['user', userId] });

The Solution //

Since invalidateQueries matches by prefix, invalidating a very general key like ['user'] affects every user query in the cache, even ones unrelated to the actual change. Invalidate the narrowest key that's genuinely affected.

Lesson Glossary

[01]staleTime

How long fetched data is considered fresh before refetch triggers will fetch it again.

Code Preview
staleTime: 60 * 1000

[02]gcTime

How long inactive, unused cached data remains in memory before being garbage collected.

Code Preview
gcTime: 10 * 60 * 1000

[03]invalidateQueries

A QueryClient method that marks matching queries stale and triggers an immediate refetch.

Code Preview
queryClient.invalidateQueries({ queryKey })

[04]Prefix Matching

TanStack Query's default query key matching behavior, affecting every key starting with the given prefix.

Code Preview
['user'] matches ['user', 1], ['user', 2]

Continue Learning