Capítulo 34 de 80

Chapter 34: Performance & Request Waterfalls

Core Idea

A request waterfall is any resource fetch that can't start until a prior, unrelated fetch finishes — the single biggest performance footgun in component-driven data fetching — and the fix is almost always structural: flatten the API, hoist/parallelize queries, or prefetch ahead of render.

Key Concepts

  • What a waterfall is: sequential round trips where step N can't begin until step N-1 resolves. Each hop costs at least one round trip of latency — on a high-latency connection (e.g. ~250ms), a 4-hop waterfall costs roughly 1000ms of pure latency versus ~500ms for a flattened 2-hop version.
  • Baseline waterfall: even without any dependent queries, there's already an implicit markup → JS → first query chain before any data can render — this is the floor, not something a single query call can remove.
  • Single-component / serial queries: a dependent query (second query needs data from the first, gated by enabled) is a textbook waterfall — fix by combining backend endpoints (e.g. one getProjectsByUserEmail instead of getUserByEmail then getProjectsByUser) when feasible, or accept the latency cost when it isn't.
  • Suspense's serial trap: multiple useSuspenseQuery calls side by side in one component run in serial, not parallel, because the first one suspends the component before the others execute — useSuspenseQueries (an array of query configs, one hook call) is the fix, restoring parallelism.
  • Nested component waterfalls: a parent query gating a child's render, where the child's own query then only starts after the parent resolves. If the child's query doesn't actually depend on parent data (just receives an already-available prop), hoist the child's query up to the parent and fetch both in parallel (or combine into useSuspenseQueries under Suspense) — this is a non-dependent nested waterfall, the cheap case to fix. A genuinely dependent nested waterfall (child needs an id computed from parent data, and only renders conditionally) usually needs an API restructure or a more advanced technique (like moving the fetch to a Server Component) rather than a simple hoist.
  • Code splitting compounds waterfalls: lazy-loading a component that itself contains a query adds a "load the JS chunk" hop before that component's query can even start, stacking on top of any data waterfall already present — a full first-page-load chain can easily reach five sequential round trips (markup → parent JS → parent query → child JS chunk → child query). Mitigating this means trading bundle size for latency: hoisting/conditionally prefetching the child's query into the parent's bundle removes the hop but grows the main bundle.

Key Takeaways

  1. Treat request waterfalls as a structural problem, not a query-configuration one — the durable fixes are API redesign (combined endpoints), query hoisting/parallelization, and prefetching, not a useQuery option.
  2. Swap side-by-side useSuspenseQuery calls for one useSuspenseQueries call the moment more than one Suspense query lives in the same component — otherwise they silently serialize.
  3. Distinguish dependent from non-dependent nested waterfalls before reaching for a fix: non-dependent ones are a cheap hoist; genuinely dependent ones usually need an API change or server-side restructuring.
  4. Periodically audit the Network tab for waterfalls — they accumulate silently as components get refactored, moved, or split without anyone noticing the new dependency chain.

Connects To

  • Dependent Queries: the canonical single-component waterfall this chapter generalizes.
  • Prefetching & Router Integration: the primary tool for flattening waterfalls without an API redesign.
  • Advanced Server Rendering: how Server Components move some of this cost server-side, where latency is lower.