Experimental React Data Fetching & Caching Best Practices
Implementation patterns for React applications that fetch and cache many API requests without overwhelming the backend. 48 rules across 8 categories, ordered by execution lifecycle impact — earlier categories cascade through everything downstream. Templates show both library-based (TanStack Query, SWR) and library-free (pure React + AbortController) implementations so the patterns are usable regardless of stack constraints.
When to Apply
- Writing or reviewing a React component that calls
fetch,useQuery,useSWR, or any data-fetching hook - Designing a list, feed, or carousel that displays many items each requiring data
- Investigating "the backend is getting hammered" or "the page loads slowly" symptoms
- Choosing between client-side fetching, route loaders, server components, or SSR
- Implementing prefetch, retry, optimistic updates, or any failure-handling logic
- Refactoring code that already does data fetching but with waterfalls, no cache strategy, or no concurrency limits
Rule Categories by Priority
| # | Category | Impact | Prefix | Rules |
|---|---|---|---|---|
| 1 | Request Orchestration | CRITICAL | orch- |
7 |
| 2 | Cache Strategy | CRITICAL | cache- |
7 |
| 3 | Backend Protection | CRITICAL | protect- |
7 |
| 4 | Prefetch & Hydration | HIGH | prefetch- |
6 |
| 5 | Failure Resilience | HIGH | resilience- |
6 |
| 6 | Feed & Carousel Patterns | MEDIUM-HIGH | feed- |
7 |
| 7 | Mutation & Invalidation | MEDIUM | mutate- |
4 |
| 8 | Component Patterns | MEDIUM | render- |
4 |
Quick Reference
1. Request Orchestration (CRITICAL)
orch-parallelize-independent-fetches— UsePromise.allfor independent requests; never serialawaitorch-batch-n-plus-one-fanout— Collapse per-row fetches via DataLoader-style batchingorch-dedupe-in-flight-requests— One in-flight request per key, shared across subscribersorch-lift-fetch-to-route-loader— Fetch in parallel with route chunk downloadorch-avoid-effect-chains— Flatten the dependency graph; only true dependencies waitorch-server-fetch-when-possible— Move fetches to RSC/server when they don't depend on client stateorch-prefer-bulk-endpoint-for-fanout— One bulk request beats N parallel requests
2. Cache Strategy (CRITICAL)
cache-deterministic-keys— Canonicalize cache keys; strip undefined, sort arrayscache-normalize-shared-entities— Store each entity once; views hold referencescache-set-stale-time— TunestaleTimeto data volatility, not refresh frequencycache-stale-while-revalidate— Render stale instantly; revalidate in backgroundcache-select-subscribed-fields— Subscribe to slices, not whole objectscache-shared-key-factory— One typed source of truth for keys; prevents read/write driftcache-tiered-stale-fresh— DifferentstaleTimeper data class (realtime, fresh, warm, cold, static)
3. Backend Protection (CRITICAL)
protect-concurrency-limit-fanout— Cap simultaneous requests withp-limit/semaphoreprotect-collapse-identical-requests— In-flight dedup at the fetch layerprotect-debounce-user-driven-fetches— Wait for user pause before firing search/filter requestsprotect-throttle-scroll-triggered— Use IntersectionObserver for viewport-triggered fetchesprotect-jittered-retry-backoff— Add random jitter to prevent thundering-herd retriesprotect-circuit-breaker— Stop calling persistently failing endpoints for a cooldown windowprotect-rate-limit-aware-client— HonorRetry-AfterandX-RateLimit-*headers
4. Prefetch & Hydration (HIGH)
prefetch-hover-intent-links— Prefetch on hover/pointerdown for instant navigationprefetch-parallel-loader-queries— Parallelize independent queries inside loadersprefetch-hydrate-server-cache— Ship server-fetched data viaHydrationBoundary/ RSCprefetch-idle-likely-next— UserequestIdleCallbackfor likely-next dataprefetch-viewport-triggered-next-page— Fire next-page prefetch viarootMarginprefetch-budget-and-priority— Tier prefetches by priority; respectSave-Data
5. Failure Resilience (HIGH)
resilience-abort-on-unmount— ForwardAbortSignalto cancel stale fetchesresilience-bounded-timeouts— Set per-endpoint timeouts viaAbortSignal.timeout()resilience-scoped-error-boundaries— One boundary per data section, not per pageresilience-stale-fallback— Render stale cache when fresh fetch failsresilience-no-auto-retry-mutations— Use idempotency keys or don't auto-retryresilience-graceful-degradation— Critical/important/decorative tiers with different failure modes
6. Feed & Carousel Patterns (MEDIUM-HIGH)
feed-virtualize-long-lists— Use TanStack Virtual for lists > 50 itemsfeed-cursor-pagination— Cursors beat offset for inserts and large offsetsfeed-split-summary-from-detail— Carousel summaries lightweight; detail on demandfeed-multi-carousel-isolation— Per-carousel error/Suspense boundaries + tiered fallbacks for homepage feedsfeed-stable-keys-across-pages— Use entity IDs as keys; never indexfeed-image-lazy-and-sized—loading="lazy"+ explicit dimensionsfeed-bounded-working-set—maxPages+ entity LRU eviction for unbounded feeds
7. Mutation & Invalidation (MEDIUM)
mutate-optimistic-updates-with-rollback— Snapshot, optimistic write, rollback on errormutate-surgical-invalidation— Invalidate specific keys, not entire treesmutate-set-data-over-invalidate— Write mutation responses directly into the cachemutate-cancel-queries-on-mutate— Cancel in-flight queries before optimistic writes
8. Component Patterns (MEDIUM)
render-stable-query-keys—useMemoobject keys to keep references stablerender-cap-fanout-in-lists— Lift fetches; batch via DataLoader; virtualizerender-suspense-per-section— One Suspense boundary per data sectionrender-colocate-fetch-with-consumer— PutuseQuerynext to its consumer, not at the root
How to Use
- Open references/_sections.md for category definitions and impact rationale
- Read individual rule files for incorrect-vs-correct code examples
- For ready-to-use scaffolds, see scaffolding templates
- The AGENTS.md navigation document (auto-generated) provides a TOC for browsing
Scaffolding Templates
Six ready-to-adapt code templates under assets/templates/:
| Template | Library deps | Purpose |
|---|---|---|
use-resource-query.template.tsx |
TanStack Query | Standard query hook with key factory, retry, abort, optional suspense |
use-resource-query-no-deps.template.tsx |
None (pure React + AbortController) | Same patterns as above, library-free: hand-rolled cache, dedup, retry with jitter, staleTime/gcTime, concurrency limit |
carousel-data-loader.template.tsx |
TanStack Query + react-error-boundary | Single carousel (summary + viewport-triggered detail) and multi-carousel feed with per-carousel failure isolation |
infinite-feed.template.tsx |
TanStack Query + TanStack Virtual | Cursor-paginated infinite feed with virtualization and bounded working set |
prefetch-link.template.tsx |
None | Hover/intent prefetch link wrapper |
request-collapser.template.ts |
None | In-flight deduplication + concurrency limit utility |
Library-free path: if you can't add TanStack Query / SWR / DataLoader to your bundle (size constraints, host-app conflicts, dependency bans), start with use-resource-query-no-deps.template.tsx — it implements the core cache/dedup/retry/abort patterns in ~250 lines using only React and the web platform. The other templates that depend on TanStack can be adapted on top of it; only the request-collapser and prefetch-link templates are zero-dep out of the box.
Reference Files
| File | Description |
|---|---|
| references/_sections.md | Category definitions, ordering, impact rationale |
| assets/templates/_template.md | Template for authoring new rules |
| metadata.json | Version, references, abstract |
Related Skills
react-optimise— General React render performance (this skill is data-fetching-specific)nextjs-bundle-optimizer— Bundle/payload optimization for Next.jsinngest-nextjs-patterns— Server-side workflow patterns (complements server-fetch guidance)