awesome-everything RU
↑ Back to the climb

Frontend Architecture

Where data fetching happens — and why it decides LCP

Crux The four places a React app can fetch data, what each costs in round-trips, and the mental model for choosing between them.
Your altitude — climbing toward senior
ZeroJuniorMiddleSenior
You are at junior altitude — the surface
◷ 8 min

A blog homepage loads in 400ms on one app, 2100ms on another. Same CDN, same database, same React version. The only difference: where the data fetch happens.

The critical path from click to content

Every page load has a critical path — a chain of sequential round-trips the browser must complete before the main content becomes visible. Largest Contentful Paint (LCP) measures when that content appears. Every unnecessary round-trip adds 50–200ms on a fast network, 500ms+ on mobile.

With a pure client-rendered SPA the chain looks like:

  1. Browser fetches index.html from CDN (~50ms)
  2. Browser downloads JS bundle (~400–600ms parse + evaluate)
  3. React mounts the component tree
  4. useEffect fires, calls /api/data (~200ms)
  5. React re-renders with data, LCP fires

Total: easily 800–2000ms on a fast connection, 3–5s on a slow one.

Client SPA — 4 sequential trips before LCP
1GET /index.html → CDN → 50ms
2GET /bundle.js → CDN → 600ms (parse + eval)
3React.mount() → CPU → 100ms
4GET /api/data → origin server → 200ms
LCP fires at ~950ms (fast network). Mobile: 3–5s.

The four locations

Modern frameworks support four fetch locations:

Build time (SSG — static generation). Data is baked into HTML at build. Zero per-request fetch cost. Stale until next build. Use for content that changes hourly or less: blog posts, marketing pages, documentation.

Server at request time (SSR or Server Components). Fetch happens on the server while generating the HTML response. The browser receives HTML with data already inside. One effective round-trip. Next.js RSC async components, Remix loaders, and getServerSideProps all use this pattern.

Streaming server-side. Server sends HTML in chunks before all data is ready. Layout shell arrives in ~100ms; slower data sections stream in as they resolve. Each React Suspense boundary is independently streamable. The user sees content much sooner even when some sections take 500ms+ to fetch.

Client after mount (CSR). useEffect, useQuery, useSWR. Page mounts blank; data arrives after JS runs. Necessary for user-interactive flows — filters, real-time updates, anything that depends on user state. But wrong for the LCP element.

The restaurant metaphor

Client fetch is ordering at a restaurant where you sit down to an empty table, study the menu for two minutes, then wait for your food. Server fetch is calling ahead: the host takes your order, you sit down to food already on the table. Streaming is the appetiser arriving instantly while the main course finishes cooking. The total kitchen time is similar, but the perceived wait is radically shorter.

Quiz

Why is client-side data fetching slower for first page load than server-side?

Quiz

A blog that publishes 5 new posts per day. Which fetch location fits best?

Order the steps

Order the network steps for a typical client-rendered React app loading a product page:

  1. 1 User clicks a link
  2. 2 Browser requests index.html from the CDN
  3. 3 Browser downloads and parses JS bundle
  4. 4 JS executes, React mounts the app
  5. 5 useEffect fires, calls fetch('/api/product/123')
  6. 6 Server responds with product JSON
  7. 7 React re-renders with data, LCP fires
Recall before you leave
  1. 01
    Name the four data-fetching locations from build-time to client-only.
  2. 02
    Why does SSG have zero per-request fetch cost?
  3. 03
    What is LCP and why does the fetch location affect it?
Recap

A page’s critical path is the chain of sequential round-trips between click and visible content. Client-rendered SPAs stack 4+ trips before LCP fires; server-side rendering collapses that to one effective trip. Streaming goes further: the layout shell arrives in under 100ms and each section streams in independently. The rule: put the LCP element’s data on the server, put user-interactive state on the client. For mostly-static content (blogs, docs) SSG with CDN caching gives near-zero TTFB at essentially zero server cost.

Connected lessons
appears again in202
Continue the climb ↑Fetch waterfalls — diagnosis and the Promise.all cure
shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.