---
title: "13 — Data Fetching Patterns — Server-First, Parallel, Preload"
uid: data-fetching-patterns
tags: ["roadmap:nextjs", "sequential", "preloading", "data-fetching", "parallel", "nextjs"]
excerpt: "Fetch on the server by default; sequential fetching creates waterfalls (sometimes wanted, often not); parallel + preload patterns collapse them and cut load time."
date: 2026-08-13T03:28:00+0000
source: https://www.aveshina.my.id/en/blog/data-fetching-patterns
---

The unintentional waterfall is the failure mode these patterns exist to prevent, and I've shipped it more than once. The model that clicked: **fetch on the server by default, fetch only where necessary, and choose between parallel (start everything at once) and sequential (one fetch depends on the next) deliberately** [1]. The unintentional waterfall is the failure mode these patterns exist to prevent.

## The principles

The roadmap lays out the recommended patterns as a short list [1]:

- **Fetch on the server.** Server Components can fetch data directly, keeping the work off the client and closer to the data source.
- **Fetch only where necessary.** Push fetching down to the component that actually uses the data, rather than hoisting everything to a parent.
- **Use streaming and Suspense** to progressively render and stream units of the UI as data resolves.
- **Choose parallel or sequential** fetching based on whether the requests are independent or dependent.
- **Preload** data to start fetches early.

The first two are the worldview; the last three are the tactics.

## Parallel vs sequential — the waterfall question

This is the central distinction. **Sequential fetching** means requests in a route depend on each other, creating a waterfall — A resolves, then B starts, then C [2]. Sometimes that's intentional: B genuinely needs A's result, or I want a condition satisfied before paying for the next fetch. Often it's accidental: a parent awaits its own fetch, then renders a child that awaits its own, serializing what could have been concurrent.

**Parallel fetching** means requests are initiated eagerly and load at the same time, reducing waterfalls and total load time [2]. For independent data, parallel is almost always correct.

```figure
<svg viewBox="0 0 720 280" xmlns="http://www.w3.org/2000/svg" class="my-6 w-full max-w-2xl" role="img" aria-label="Sequential vs parallel fetching. Top timeline: fetch A runs, then B starts after A, then C after B — a waterfall, total time = A + B + C. Bottom timeline: A, B, C all start at once and run concurrently — total time = max(A, B, C).">
  <g font-family="ui-sans-serif, system-ui, sans-serif" text-rendering="geometricPrecision">

    <!-- Sequential -->
    <text x="360" y="24" font-size="12" font-weight="700" fill="#7f1d1d" text-anchor="middle">Sequential — waterfall (A then B then C)</text>
    <rect x="60" y="44" width="100" height="28" rx="4" fill="#fee2e2" stroke="#dc2626"/>
    <text x="110" y="62" font-size="10" fill="#7f1d1d" text-anchor="middle">fetch A</text>
    <rect x="160" y="44" width="100" height="28" rx="4" fill="#fee2e2" stroke="#dc2626"/>
    <text x="210" y="62" font-size="10" fill="#7f1d1d" text-anchor="middle">fetch B</text>
    <rect x="260" y="44" width="100" height="28" rx="4" fill="#fee2e2" stroke="#dc2626"/>
    <text x="310" y="62" font-size="10" fill="#7f1d1d" text-anchor="middle">fetch C</text>
    <line x1="60" y1="86" x2="360" y2="86" stroke="#dc2626" stroke-width="1.5"/>
    <text x="210" y="104" font-size="10" font-style="italic" fill="#7f1d1d" text-anchor="middle">total = A + B + C</text>

    <!-- Parallel -->
    <text x="360" y="150" font-size="12" font-weight="700" fill="#052e16" text-anchor="middle">Parallel — concurrent (A, B, C together)</text>
    <rect x="60" y="170" width="100" height="28" rx="4" fill="#dcfce7" stroke="#16a34a"/>
    <text x="110" y="188" font-size="10" fill="#052e16" text-anchor="middle">fetch A</text>
    <rect x="60" y="202" width="120" height="28" rx="4" fill="#dcfce7" stroke="#16a34a"/>
    <text x="120" y="220" font-size="10" fill="#052e16" text-anchor="middle">fetch B</text>
    <rect x="60" y="234" width="90" height="28" rx="4" fill="#dcfce7" stroke="#16a34a"/>
    <text x="105" y="252" font-size="10" fill="#052e16" text-anchor="middle">fetch C</text>
    <line x1="60" y1="270" x2="180" y2="270" stroke="#16a34a" stroke-width="1.5"/>
    <text x="430" y="220" font-size="10" font-style="italic" fill="#052e16" text-anchor="middle">total = max(A, B, C)</text>
  </g>
</svg>
```

The App Router helps here via Promise passthrough: a Server Component can start a fetch (returning a Promise) and pass that Promise to a child as a prop, rather than awaiting it in the parent. The child awaits the Promise; the parent doesn't block on it. This keeps independent fetches concurrent.

## Preloading — starting fetches early

The **preload pattern** is the optimization on top of parallel fetching [3]. A preload function kicks off a fetch eagerly — before the component that needs the data even mounts — so the request is already in flight when the component renders. The roadmap frames it as a pattern, not an API: the function can have any name, and the payoff is hiding latency by overlapping fetch time with render time [3].

```
// a preload function — pattern, not API
import { getItem } from '@/lib/data';

export const preload = (id: string) => {
  void getItem(id); // start the fetch eagerly, ignore the promise here
};

export default async function Page({ params }: { params: { id: string } }) {
  preload(params.id); // kick off early
  // …other work…
  const item = await getItem(params.id); // already in flight, returns fast
  return <ItemView item={item} />;
}
```

The combined effect of parallel + preload: waterfalls collapse, total load time drops, and the user sees content sooner.

## How I use this

My defaults: Server Components everywhere data is needed; fetch pushed down to the component that owns the data; independent fetches run in parallel (often via Promise passthrough rather than awaiting in the parent). I reach for sequential deliberately when a fetch genuinely depends on another's result. And for slow, predictable fetches, I add a preload to start them early. The discipline is to spot the accidental waterfall — a parent awaiting something a child could have started — and collapse it.

## References

[1] Vercel, "Patterns and best practices," Next.js Docs, 2024. [Online]. Available: [https://nextjs.org/docs/14/app/building-your-application/data-fetching/patterns#fetching-data-on-the-server](https://nextjs.org/docs/14/app/building-your-application/data-fetching/patterns#fetching-data-on-the-server)

[2] Vercel, "Parallel and sequential data fetching," Next.js Docs, 2024. [Online]. Available: [https://nextjs.org/docs/14/app/building-your-application/data-fetching/patterns#parallel-and-sequential-data-fetching](https://nextjs.org/docs/14/app/building-your-application/data-fetching/patterns#parallel-and-sequential-data-fetching)

[3] Vercel, "Preloading data," Next.js Docs, 2024. [Online]. Available: [https://nextjs.org/docs/14/app/building-your-application/data-fetching/patterns#preloading-data](https://nextjs.org/docs/14/app/building-your-application/data-fetching/patterns#preloading-data)

[4] Vercel, "Fetching data," Next.js Docs, 2024. [Online]. Available: [https://nextjs.org/docs/app/getting-started/fetching-data](https://nextjs.org/docs/app/getting-started/fetching-data)

```quiz
Q: What's the default recommendation for where to fetch data in Next.js App Router?
- In Client Components, after mount
- On the server, in Server Components, only where the data is used
correct: 1
explain: The recommended pattern is server-first fetching in Server Components, pushed down to the component that owns the data rather than hoisted to a parent.

Q: Sequential data fetching creates waterfalls. When is that actually desirable?
- Never — it's always a bug
- When one fetch genuinely depends on another's result, or a condition must be satisfied before paying for the next fetch
correct: 1
explain: Sequential is correct when there's a real dependency. The failure mode is unintentional waterfalls where independent fetches get serialized accidentally.

Q: What does the preload pattern do?
- Caches the fetch result permanently
- Starts a fetch eagerly before the component that needs it renders, hiding latency by overlapping fetch and render time
correct: 1
explain: Preload kicks off a fetch early so it's in flight by the time the consuming component renders. It's a pattern (any function name), not a built-in API.

Q: Passing a Promise from a parent Server Component to a child (rather than awaiting it in the parent) helps because…
- it skips the fetch entirely
- it lets independent fetches run concurrently instead of the parent blocking on them before the child even mounts
correct: 1
explain: Promise passthrough keeps the parent from blocking on a fetch the child owns, so independent fetches stay concurrent and waterfalls collapse.
```
