13 — Data Fetching Patterns — Server-First, Parallel, Preload

5 min read

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.

Sequential — waterfall (A then B then C) fetch A fetch B fetch C total = A + B + C Parallel — concurrent (A, B, C together) fetch A fetch B fetch C total = max(A, B, C)

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

[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

[3] Vercel, "Preloading data," Next.js Docs, 2024. [Online]. Available: 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

Knowledge check · Question 1 of 4

What's the default recommendation for where to fetch data in Next.js App Router?

Comments

Leave a Comment

You must be signed in to comment

0 Comments

No comments yet. Be the first to comment!