Sequential API requests that could run in parallel add their round-trip times together: if fetching a user profile takes 200ms and fetching their posts takes 150ms, a waterfall loads in 350ms; a parallel fetch completes in 200ms. Across a dashboard that chains four independent requests, this compounds to 600–800ms of avoidable latency before the page renders any data. For server components in Next.js, parallel fetching is a single line change (Promise.all). Request waterfalls are one of the most common and highest-impact performance issues in data-heavy applications, and they scale worse as API latency grows. ISO 25010 performance-efficiency.time-behaviour requires minimizing response time for the same work.
High because each chained sequential request multiplies the total page load latency by the number of chain links, with real-world impact of hundreds of milliseconds to seconds.
Replace await fetch() chains with Promise.all for any requests that don't depend on each other's return values. Identify the true dependency graph: if request B needs an ID from request A, only A must complete first; B and C can still be parallelized.
// Before — sequential waterfall: 200ms + 150ms + 120ms = 470ms total
const user = await fetchUser()
const posts = await fetchPosts(user.id)
const settings = await fetchSettings(user.id)
// After — user first (needed for ID), then posts + settings in parallel
const user = await fetchUser() // 200ms
const [posts, settings] = await Promise.all([
fetchPosts(user.id), // 150ms }
fetchSettings(user.id) // 120ms } run together = 150ms
])
// Total: 200ms + 150ms = 350ms (saves 120ms per request)
// For fully independent requests, parallelize everything
const [products, categories, featuredBanner] = await Promise.all([
fetchProducts(),
fetchCategories(),
fetchFeaturedBanner()
])
ID: performance-load.rendering.no-waterfall-requests
Severity: high
What to look for: For each page that fetches data, trace the data fetching pattern on page load. Count the number of sequential fetch calls (where request B waits for request A to complete before starting, and request B does not depend on A's response data). Identify chains of 2+ sequential requests where at least 2 could run in parallel via Promise.all. Report: "X pages fetch data, Y have waterfall patterns with Z parallelizable requests."
Pass criteria: No more than 1 page has a waterfall pattern with 2+ parallelizable requests. If a page needs 3+ pieces of data, at least 2 are fetched concurrently via Promise.all, Promise.allSettled, or equivalent parallel pattern. Report: "X of Y data-fetching pages use parallel requests where possible."
Fail criteria: 2 or more pages have waterfall request patterns where requests are chained sequentially despite having no data dependency between them, adding unnecessary latency.
Skip (N/A) when: No data fetching is detected in the project (0 fetch(), axios, useSWR, useQuery, getServerSideProps, or server component data fetching patterns found).
Detail on fail: "3 of 5 data-fetching pages have waterfalls — dashboard chains 3 independent requests sequentially (user, settings, posts) instead of Promise.all" or "Product page fetches product, then related products, then reviews one at a time — 3 sequential requests that could be 1 parallel batch"
Remediation: Parallelize requests:
// Before — waterfall (sequential)
const user = await fetch('/api/user').then(r => r.json())
const settings = await fetch(`/api/user/${user.id}/settings`).then(r => r.json())
const posts = await fetch(`/api/user/${user.id}/posts`).then(r => r.json())
// After — parallel (concurrent)
const [user, settings, posts] = await Promise.all([
fetch('/api/user').then(r => r.json()),
fetch('/api/user/settings').then(r => r.json()),
fetch('/api/user/posts').then(r => r.json())
])
For Next.js with required user ID:
// After — get user first, then parallelize others
const user = await fetch('/api/user').then(r => r.json())
const [settings, posts] = await Promise.all([
fetch(`/api/user/${user.id}/settings`).then(r => r.json()),
fetch(`/api/user/${user.id}/posts`).then(r => r.json())
])