Client-Side Caching Strategies
Client-side caching reduces repeated network requests, hides latency, and makes screens feel stable when users move back and forth. The hard part is not storing data; it is deciding freshness, scope, invalidation, and what must never be cached.
TTL Cache
A minimal cache stores the value plus an expiry timestamp. Reads return cached data only while it is fresh.
type CacheEntry<T> = {
data: T;
expiresAt: number;
};
export class TtlCache<T> {
private entries = new Map<string, CacheEntry<T>>();
get(key: string): T | null {
const entry = this.entries.get(key);
if (!entry) return null;
if (Date.now() > entry.expiresAt) {
this.entries.delete(key);
return null;
}
return entry.data;
}
set(key: string, data: T, ttlMs: number): void {
this.entries.set(key, { data, expiresAt: Date.now() + ttlMs });
}
invalidate(key: string): void {
this.entries.delete(key);
}
}
Use short TTLs for frequently changing data like notifications and longer TTLs for stable reference data like country lists.
Stale While Revalidate
Stale-while-revalidate returns cached data immediately, then fetches fresh data in the background. The user avoids a blank loading state, and the UI updates when the new response arrives.
export async function staleWhileRevalidate<T>(
key: string,
cache: TtlCache<T>,
fetchFresh: () => Promise<T>,
onFresh: (data: T) => void,
) {
const cached = cache.get(key);
if (cached) {
void fetchFresh().then((fresh) => {
cache.set(key, fresh, 30_000);
onFresh(fresh);
});
return cached;
}
const fresh = await fetchFresh();
cache.set(key, fresh, 30_000);
return fresh;
}
Libraries such as TanStack Query and SWR formalize this pattern with query keys, stale time, background refetching, retries, deduplication, and mutation invalidation. After a mutation, invalidate the exact query keys that became stale instead of clearing the whole cache.
WARNING
Never cache user-specific private data in a shared cache layer. Always scope the cache key to the user session.
Further Learning
Search these terms to go deeper:
- “stale while revalidate frontend” — immediate stale reads with background refresh
- “TanStack Query query keys” — cache identity and invalidation design
- “SWR data fetching strategy” — lightweight server-state caching patterns
- “frontend cache invalidation mutation” — keeping cached data correct after writes