SevyDevy

Guide · Frontend

Rendering Patterns Explained: When to Use CSR, SSR, SSG and ISR

27 min readUpdated By Saket Bhatnagar

Researched and drafted with AI, reviewed and edited by Saket Bhatnagar.

In short

  • SSR vs CSR is a choice about where a page's HTML, the text that describes what is on screen, gets built. SSR (server-side rendering) builds it on the server for each request. CSR (client-side rendering) builds it in the browser with JavaScript.
  • SSG (static site generation) builds pages once, before anyone asks. ISR (incremental static regeneration) also serves prebuilt pages, then rebuilds a page when someone visits after its timer runs out, or after your code says its data changed.
  • Public pages like blog posts and product pages should arrive as finished HTML from SSG, ISR or SSR. Search engines and link previews, the title and photo a chat app shows for a shared link, read them. Private pages behind a login can render in the browser.
  • Most real sites mix all four, page by page. Ask who reads the page, how many versions it has, and how often its data changes.
  • Beginners can stop after the page-by-page plan. Engineers then get what we measured on Next.js 16.4.2: stale visits after the timer and failed rebuilds retried once per timer. They also get React 19.3.1 hydration mismatches, where server HTML and browser code disagree, and why classic ISR code fails once Next.js's newer Cache Components mode is on.

What goes wrong when an online store renders every page in the browser?

Its pages reach the browser almost empty, and the content appears only after JavaScript runs. Search crawlers, the programs that read pages for search engines, then have extra work to do. Some bots never run JavaScript, so they see only the empty shell. This is what the SSR vs CSR choice is about: who builds the page, the server or the browser.

Picture a common setup. An online shoe store sells about 2,400 styles, runs a blog about fit and shoe care, and has search and account pages. Two years ago the team built it as a single-page app. The setup is made up, but the problem is real.

A single-page app, or SPA, sends one almost empty HTML file. JavaScript then builds every screen inside the browser. Once it has loaded, moving from one shoe to the next feels instant, because the app never asks the server for a whole new page.

Then the marketing team brings a list of problems (all numbers made up):

  • New blog posts show up in search later than posts on the old server-rendered blog did.
  • Product links shared in chat apps show a bare web address, with no shoe photo and no title.
  • On a mid-range phone over 4G, a product page stays blank for about 4 seconds.
  • During the Diwali sale, prices change twice a day, and nobody knows how fresh each page must be.

One engineer wants to render everything on the server. Finance worries about server bills on sale days. So the team walks through the site page by page and gives each page its own answer. This guide follows that walk.

Which words do you need before comparing rendering patterns?

Rendering means turning your data and code into HTML that a browser can show. The real design question is where that happens, and when. Six more words come up again and again.

HTML. HTML is the text that describes what is on a page: headings, images, buttons and prices. Example: a heading that says Runner 7, and a line that says Rs 2,499.

JavaScript bundle. The bundle is the JavaScript file, or set of files, the browser downloads to run your app. The store's old bundle was about 900 KB (made up), which is heavy for a cheap phone.

Hydration. Hydration is when JavaScript in the browser attaches click handlers and state, the data a component remembers, to HTML that already came from the server. Until it finishes, the page looks ready, but the size picker may not respond to taps.

CDN. A CDN, or content delivery network, is a group of servers in many cities that keep copies of files close to users. Any page that is a plain, finished file can sit on a CDN and load fast.

TTFB and FCP. Time to first byte (TTFB) is how long the browser waits for the first byte of the page. First contentful paint (FCP) is the moment real content, like the shoe name, first appears.

INP. Interaction to Next Paint (INP) measures how quickly a page responds to taps and clicks during a visit. Google's web.dev guide calls 200 milliseconds or less good.

What are CSR, SSR, SSG and ISR in simple words?

They are four answers to one question: on which machine, and at what time, does the HTML get built? CSR builds it in the browser, SSR on the server per request, SSG before anyone asks, and ISR before anyone asks, with refreshes later.

Picture a phoolwala, a flower seller, with a stall at the gate of a busy temple. He sells garlands in four ways, and each one maps to a pattern.

  • Ready-strung garlands hang on hooks, made before dawn. You pay and leave in seconds. That is SSG, static site generation: the page was built before you asked.
  • A buyer wants a special mix of roses and tuberose, so he strings it while she waits. That is SSR, server-side rendering: built fresh for your request, so you wait a little.
  • Another buyer takes a bag of loose flowers, needle and thread, and strings the garland at home. That is CSR, client-side rendering: the browser gets the parts and does the work.
  • Hanging garlands wilt, so after a while he restrings them. That is ISR, incremental static regeneration: ready-made pages that get rebuilt after a set time.

The loose-flower buyer is the case to watch. If her hands are quick, it works well. A slow, cheap phone has slow hands, and it strings the page for a long time while the shopper stares at a blank screen.

Where the picture breaks: the phoolwala would never hand a buyer yesterday's garland. ISR does exactly that after its timer runs out. Visitors keep getting the old page until the new one is ready.

The four rendering patterns side by side
CSRSSRSSGISR
Where the HTML is builtIn the browserOn the serverOn the build machineOn the server, then cached
When it is builtAfter the JavaScript loadsOn every requestOnce, at deploy timeAt deploy or first visit, then on a visit after the timer, or on demand
First content on a slow phoneSlow: waits for JavaScript and dataAs soon as the server answersFast: a ready file, often from a CDNFast: a cached file
What crawlers get firstAn almost empty shellFull HTMLFull HTMLFull HTML
Server work per visitServe files, plus API callsA full page buildServe a fileServe a cached file, plus a rebuild after the timer
FreshnessLive, from the APILive on every requestAs fresh as the last deployUp to your timer on busy pages; on quiet pages, as old as the last rebuild

SSR vs CSR: what is the difference, and which is better?

The difference is who builds the page. With SSR, the server sends finished HTML. With CSR, the browser gets an empty shell and builds the page with JavaScript. Neither wins everywhere, so the answer depends on the page.

Here is how the old CSR store loaded a product page on a first visit. Count the steps before the shopper sees a single shoe.

A product page in the old CSR store

Five steps happen before any shoe appears, and the phone does the heavy work. Sizes and times are made up.

Diagram connections:

  • Shopper taps a shoe link to Send an empty HTML shell
  • Send an empty HTML shell to Download the JS bundle
  • Download the JS bundle to Run the app code
  • Run the app code to Fetch the shoe's data
  • Fetch the shoe's data to Show the product
  • Shopper taps a shoe link: Phone → CDN
  • Send an empty HTML shell: CDN → Phone: one empty div and a script tag
  • Download the JS bundle: CDN → Phone: about 900 KB of JavaScript
  • Run the app code: Browser → JS engine: parse and start the app
  • Fetch the shoe's data: Phone → Product API: name, price, photos
  • Show the product: Browser → Screen: first content at about 4 s

With SSR, the server fetches the shoe's data, builds the HTML and sends it in one response. The shopper sees the shoe as soon as that HTML arrives. JavaScript then hydrates the page so the size picker and the add-to-cart button work.

Each side has real strengths and costs:

  • SSR usually shows content sooner on slow phones, because the phone does not build the page first. A slow server or database can cancel that lead.
  • SSR gives crawlers and link-preview bots complete HTML in the very first response.
  • SSR costs server work on every visit. A slow database call delays the whole page, which raises TTFB.
  • CSR keeps hosting simple: plain files on a CDN, plus an API for data.
  • CSR makes later moves between screens fast, once the app has loaded.
  • Both can ship too much JavaScript, and heavy hydration makes taps feel slow.

The rendering guide on web.dev warns that server-rendered pages can appear loaded and interactive but cannot yet respond to input. The HTML is there, but hydration has not finished.

Search adds another reason to care. Google Search's documentation says it processes JavaScript pages in three phases: crawling, rendering and indexing. A page may wait in a render queue before a headless browser, one with no screen, runs its JavaScript.

The same page still calls server-side rendering or pre-rendering a great idea. It makes the site faster for users and crawlers, and not all bots can run JavaScript. That fits the bare chat previews: a preview bot that skips JavaScript sees only the empty shell.

Check what arrives before any JavaScript runs

You can see a page the way a non-JavaScript bot sees it. Fetch the raw HTML with curl and count a short piece of text from one value, like the shoe name.

bash
curl -s https://YOUR-SITE/shoes/SLUG | grep -c "SHOE NAME"

On our local test app, the ISR shoe page printed 1 and the client-rendered order list printed 0. Any number above 0 means the text is there. A 0 means it arrives only after JavaScript runs, so a bot that skips JavaScript never sees it.

Keep the search short. React inserts hidden comment markers between neighbouring pieces of text, so a phrase joining the name and price, like Runner 1, Rs 2499, printed 0 there.

When should a page use static site generation?

Use SSG when a page looks the same for every visitor and changes rarely, like a blog post or a size guide. It is built once at deploy time and served as a plain file, often from a CDN.

The store's blog has about 180 posts, such as how to measure your foot at home. A post changes maybe once a month. Every visitor sees the same words, so there is no reason to build it per request.

The team moves the blog to SSG. A build reads every post and writes one HTML file per post. When someone publishes or edits a post, the site rebuilds and the CDN gets the new files. Crawlers and chat previews now get full HTML at once.

SSG has a limit, and it shows up on product pages. The web.dev guide notes that static rendering must generate a file for every possible URL, which gets hard when you cannot predict them. The store has 2,400 product pages whose prices move during sales.

How does incremental static regeneration keep product pages fresh?

ISR serves a ready-made page from a cache. After a set time, the next visit still gets the old page, and that visit starts a rebuild in the background. Visitors after the rebuild get the new page.

The store now runs on Next.js, a React framework that supports all four patterns. Its ISR guide describes exactly these steps, with the timer set by a value called revalidate.

Pause and predict. The timer has run out, the price has changed, and two shoppers open the same shoe page a moment apart. Which of them sees the new price?

ISR on a product page

Every shopper gets a cached page. After the timer, visits keep getting the old copy until the background rebuild finishes.

Diagram connections:

  • Shopper opens a shoe page to Cached copy over 10 min old?
  • Cached copy over 10 min old? to Send the cached page (No)
  • Cached copy over 10 min old? to Send the old page anyway (Yes)
  • Send the old page anyway to Next.js rebuilds the page (In the background)
  • Next.js rebuilds the page to Replace the cached copy
  • Shopper opens a shoe page: Phone → Page cache
  • Cached copy over 10 min old?: Page cache → revalidate setting
  • Send the cached page: Page cache → Phone: marked HIT
  • Send the old page anyway: Page cache → Phone: marked STALE
  • Next.js rebuilds the page: Next.js server → Catalog DB: latest price
  • Replace the cached copy: Next.js server → Page cache: later visitors

In our test, neither. On Next.js 16.4.2, with a 10-second copy of the page, two quick requests after the timer both got the old price, marked STALE. The request after them got the new price.

So a page can be as old as the time since its last rebuild. In a second run, nobody visited for 45 seconds, and the next two requests got a copy built 46 seconds earlier. Without on-demand revalidation, covered later, a shoe page nobody opened overnight can show last evening's price to the first morning shopper.

This trick is older than ISR. RFC 5861, published in May 2010, defines an HTTP cache setting called stale-while-revalidate. It lets a cache serve a stale response for a set number of seconds while it fetches a fresh one in the background.

Next.js speaks the same language on the wire. Our 600-second product page came back with Cache-Control: s-maxage=600, stale-while-revalidate=31535400. The s-maxage part tells shared caches, such as a CDN, how many seconds the page counts as fresh. The second number is one year in seconds minus the 600.

Which pages need SSR, and which can stay client-side?

Use SSR for public pages that must be fresh on every request and have too many versions to build ahead, like search results. Keep pages client-side when they are private, behind a login, and full of interaction.

Search results: SSR

The search page has endless versions. Every query, size filter and sort order makes a new address, so nothing can be built ahead. Results also depend on live stock, and shoppers send result links to friends.

So search is server-rendered. In Next.js, a page that reads searchParams, the part of the address after the question mark, renders at request time. Each search returns full HTML with the first 24 results, and the filters then update without a full reload.

The team adds a noindex robots tag to search pages, a tag that asks search engines to drop a page from their results. Google may skip rendering once it sees noindex, and warns that changing or removing the tag with JavaScript may not work. So put it in the server HTML.

Account and orders: CSR inside a static shell

The account area shows orders, saved addresses and the wishlist. It sits behind a login, so no crawler should ever see it. Each shopper's data is different, and the pages are full of buttons and forms.

The team keeps these pages client-side. The header, menu and page layout arrive as static HTML. A small client component then calls the orders API with the shopper's login and fills in the list.

In Next.js, pages and layouts are server components by default. A file that starts with the use client line becomes a client component: its JavaScript ships to the browser, so it can hold state and handle taps.

Client components still get their first HTML on the server. Only data fetched after the page loads, like the order list, is truly built in the browser. Our test page's raw HTML held its loading message but not the order.

One page, two patterns

Patterns can also mix inside a page. The product page is ISR, but the size picker and add-to-cart button are small client components. The Next.js docs suggest marking only interactive parts this way, which keeps the JavaScript bundle smaller.

How do you choose a rendering pattern for each page?

Ask three questions for every page. Can you list its addresses and show everyone the same thing? Does it change often? Is it private? The answers point to one pattern.

Here is the decision path the team used. Start at the top and follow one page down to its pattern.

Choosing a pattern for one page

Three questions per page. Most public pages end at SSG or ISR, and only pages with endless versions need SSR.

Diagram connections:

  • A page to plan to Known URLs, same for all?
  • Known URLs, same for all? to Changes more than weekly? (Yes)
  • Known URLs, same for all? to Private, behind a login? (No)
  • Changes more than weekly? to ISR: rebuild after a timer (Yes)
  • Changes more than weekly? to SSG: build once (No)
  • Private, behind a login? to CSR: build in the browser (Yes)
  • Private, behind a login? to SSR: build per request (No)
  • A page to plan: Run every route through these questions
  • Known URLs, same for all?: You can list the pages, no per-user data
  • Changes more than weekly?: Prices, stock, new arrivals
  • Private, behind a login?: No search engine needs to read it
  • SSG: build once: Blog posts and size guides
  • ISR: rebuild after a timer: Home, product and category pages
  • SSR: build per request: Search results
  • CSR: build in the browser: Account, orders, wishlist, cart

Here is the store's final plan, page by page.

The store's rendering plan, page by page (refresh times made up)
PagePatternWhen it refreshesWhy
Blog posts and size guidesSSGOn publish or editSame for everyone, changes rarely
Home pageISR15 minutes after the last rebuild, on the next visitSale banners change, but it is public
Product pages (2,400)ISRAfter 10 minutes, and at once on a price changePublic, searched for, prices move
Category pagesISRAfter 30 minutesThe main landing pages from search
Search resultsSSREvery requestEndless versions, live stock
Account, orders, cartCSR in a static shellLive API callsPrivate, personal, no search need

After the switch, the same mid-range phone showed a product page in about 1.3 seconds instead of 4 (made up). Shared links showed the shoe photo and title again. Only search pages cost a full server build per visit.

How do you build ISR product pages in Next.js 16?

You export a revalidate timer and a generateStaticParams function from the page file. This is the classic way, and it works in Next.js 16 as long as Cache Components, covered in the next section, is off.

Here is a trimmed version of the product page. Both helpers, getShoe and getBestSellerSlugs, are made up; they read the store's catalog database. A slug is the readable part of a web address, such as runner-7.

typescript
// app/shoes/[slug]/page.tsx
import { notFound } from 'next/navigation'
import { getShoe, getBestSellerSlugs } from '@/lib/catalog'

export const revalidate = 600 // rebuild at most every 10 minutes

export async function generateStaticParams() {
  const slugs = await getBestSellerSlugs(300)
  return slugs.map((slug) => ({ slug }))
}

export default async function ShoePage({ params }: { params: Promise<{ slug: string }> }) {
  const { slug } = await params
  const shoe = await getShoe(slug)
  if (!shoe) notFound()
  return <h1>{shoe.name}, Rs {shoe.price}</h1>
}

Three lines do the real work. The revalidate value of 600 seconds is the ISR timer. The generateStaticParams function lists the 300 bestsellers to build at deploy time, so the build stays short.

The other 2,100 shoes are built on their first visit, then cached like the rest. That is the default, because a setting called dynamicParams starts as true. Finally, notFound sends a real 404 when a slug does not exist.

We built this exact file with next build and ran it on next start, Next.js's own production server. The x-nextjs-cache header, which Next.js documents, tells you how the cache treated each request.

What the product page did on Next.js 16.4.2, one next start server (timer rows used a 10-second copy)
Requestx-nextjs-cacheWhat came back
A shoe in the 300 bestsellersHITThe page built during next build
A shoe outside the 300, first visitMISSA page built during that request
The same shoe, second visitHITThe copy cached on the first visit
A slug that does not existMISSA 404 page with HTTP status 404
The same missing slug, second visitHITThe cached 404, still 404 after we added that shoe
Two quick visits just after the timerSTALE, then STALEThe old price both times
The next visit, a moment laterHITThe new price

What just happened? The timer never refreshes a page by itself. A visit after the timer starts the rebuild, and visits during the rebuild still get the old page. A page nobody visits is never rebuilt, so the first shopper after a quiet spell gets that old copy, which is why checkout checks the price again.

Refreshing a page the moment a price changes

During the Diwali sale, the pricing team cannot wait 10 minutes after a price cut. Next.js also offers on-demand revalidation: your code marks a page stale the moment its data changes. Here is the function behind the price editor.

typescript
// app/admin/actions.ts
'use server'

import { revalidatePath } from 'next/cache'
import { savePrice } from '@/lib/catalog'
import { getStaffUser } from '@/lib/auth'

export async function updatePrice(slug: string, priceInRupees: number) {
  const user = await getStaffUser() // checks the login cookie
  if (!user?.canEditPrices) throw new Error('Not allowed')
  if (!Number.isInteger(priceInRupees) || priceInRupees <= 0) throw new Error('Bad price')
  await savePrice(slug, priceInRupees)
  revalidatePath(`/shoes/${slug}`) // mark just this page stale
}

The use server line makes every exported function in the file a server action: a function that runs only on the server but can be called from the browser. That second part is why the function checks the login and the price before saving.

Next.js's data security guide says server actions are reachable by a direct POST request, so each one must verify the caller. It also says to validate every argument, because the browser can send any value. In our test, the call without a staff login threw Not allowed, and the page kept Rs 2,499. With a staff login, a price of minus 5 and a non-number both threw Bad price.

After saving, revalidatePath marks that one shoe page stale. The Next.js ISR guide says the next request to that route triggers the rebuild. In our test, that request came back MISS, built during the request with the new price. The docs list a REVALIDATED header value for on-demand rebuilds; on 16.4.2 we saw MISS.

Call it when you add a shoe, too. Not-found pages are cached like any other, so a slug someone tried before launch kept its 404. In our test, revalidatePath fixed it on the next request; without it, the 404 stayed until a visit after the timer rebuilt the page.

What changes in Next.js when Cache Components is turned on?

The product page above stops building. With cacheComponents set to true, Next.js 16.4.2 refused our file and said the revalidate route setting is not compatible with Cache Components.

This matters today, not only in Next.js 17. The 16.4 post says new apps from create-next-app get Cache Components by default. We found the cacheComponents and partialPrefetching lines in the create-next-app 16.4.2 package itself.

In that mode you delete export const revalidate. You mark the cached data or component with the use cache directive and set its lifetime with cacheLife, as the Next.js migration guide shows. The dynamicParams setting is not allowed either.

The idea of ISR stays the same: serve a cached page, refresh it later. Only the code changes. Check which mode your project uses before you copy any ISR example, including ours.

What can go wrong with each rendering pattern?

Each pattern fails in its own way. CSR can leave crawlers and slow phones with blank pages, SSR can be slow and costly, and SSG and ISR can serve stale or wrongly shared content.

  • Hydration mismatch. The server HTML and the browser's first render disagree, for example text that depends on the time. React's docs say to fix every mismatch: at best it slows the page, at worst handlers attach to wrong elements.
  • What React 19.3.1 did in our Chromium test. A text mismatch made React throw away that server HTML and build it again in the browser, logging error 418 in production. An attribute-only mismatch was never patched, and only development mode warned.
  • A slow API under SSR. The whole page waits for the slowest call. Streaming helps: the server sends the page frame first, then the slow parts as they arrive.
  • A late not-found while streaming. Once streaming starts, the status code is already 200. The Next.js docs say a 404 streamed after that gets a noindex tag instead of a 404 status.
  • Personal data in a cached page. If an ISR page greets a shopper by name, every visitor sees that greeting. Keep anything per-user out of SSG and ISR pages.
  • Several servers under ISR. The Next.js docs say each instance keeps its own ISR cache by default, so on-demand revalidation clears only the instance that receives the call. A shared cache handler fixes this.
  • A failed rebuild. If the rebuild throws, Next.js keeps serving the last good page, as its docs promise. The docs say it retries on the next request. In our 16.4.2 test it retried once per 10-second timer window instead.
  • Too much client JavaScript. Every client component adds to the bundle and to hydration time, on every pattern, not just CSR.

That retry rhythm is undocumented as of 19 October 2026; pin your version. Alert on rebuild errors too, because the page looks fine while it quietly stays old. The header will not warn you: while rebuilds failed, it said HIT, with one STALE per timer window. Only the server log showed the errors.

How do you check which pattern a page really uses?

Ask the server, not the config file. The curl check earlier shows what a crawler gets; this one shows how the Next.js cache treated the request.

bash
# How did the Next.js cache treat this request?
curl -s -D - -o /dev/null https://YOUR-SITE/shoes/SLUG | grep -i -E "x-nextjs-cache|cache-control"

On our local next start server, the ISR shoe page printed x-nextjs-cache: HIT and Cache-Control: s-maxage=600, stale-while-revalidate=31535400.

The search page printed no x-nextjs-cache line, only Cache-Control: private, no-cache, no-store, max-age=0, must-revalidate. That is the sign of a page rendered for each request. We ran these against next start only; a CDN in front may add, change or remove headers.

Then measure. Watch TTFB and FCP for the first view, and INP for taps, on a cheap phone, before and after any change.

How do you explain rendering patterns in a frontend system design interview?

Walk through the site page by page, and give each page a pattern with a reason tied to who reads it and how fresh it must be.

  1. List the page types

    Name the main routes: home, listing, detail, search, account, checkout. Say which are public and which sit behind a login.

  2. Name the readers

    For each page, say whether crawlers, link previews or only logged-in users read it. Public, searchable pages need HTML in the first response.

  3. Count versions and change rate

    Ask how many versions a page has and how often its data changes. Few versions that change rarely point to SSG, frequent changes point to ISR.

  4. Pick a pattern and a refresh rule

    Give each page CSR, SSR, SSG or ISR, plus a refresh rule, such as after 10 minutes or on a price change.

  5. Guard the risky data

    Check stock and price again at checkout. Keep personal data out of shared caches. Make every private API and server action check the login.

  6. Say what you would measure

    Name TTFB, FCP and INP on a low-end phone, plus server cost per visit. Offer to switch a page's pattern if the numbers say so.

Questions people ask

Is SSR better than CSR for SEO?
Usually, yes, for public pages. SSR sends full HTML in the first response, so crawlers and link-preview bots see the content at once. Google Search can render JavaScript, but pages may wait in a render queue, and not all bots run JavaScript. SSG and ISR give the same head start with less server work per visit.
Does Google index client-side rendered pages?
Yes, it can. Google Search crawls the page, queues it for rendering, runs its JavaScript in a headless Chromium browser, and then indexes the result. The catch is delay and risk: rendering may happen later, and other bots may never run your JavaScript. Google's own documentation still calls server-side rendering or pre-rendering a great idea.
What is hydration in SSR?
Hydration is the step where JavaScript in the browser attaches click handlers and state to HTML that came from the server. Before it finishes, the page looks ready but may not respond to taps. Shipping less client JavaScript shortens this gap, which is why frameworks let you mark only interactive parts as client components.
What is the difference between SSG and ISR?
SSG builds a page once at deploy time, and it stays the same until the next deploy. ISR also serves a prebuilt page, but rebuilds it on the first visit after a set time, or after your code marks it stale. Use SSG for content that rarely changes, and ISR for public pages whose data moves, like prices.
Can one website use CSR, SSR, SSG and ISR together?
Yes, and most real sites should. Rendering is chosen per page, and frameworks like Next.js let you set it route by route. A store can use SSG for its blog, ISR for products, SSR for search and CSR for account pages. Even one page can mix patterns, such as an ISR product page with a client-side size picker.
Is ISR only a Next.js feature?
The name is best known from Next.js, but the idea is older. RFC 5861, from 2010, defines the stale-while-revalidate cache setting, which lets a cache serve a slightly old response while it fetches a fresh one in the background. Whether your CDN or framework supports it is something to check in its own docs.
What is streaming SSR?
Streaming SSR sends the page's HTML in pieces instead of waiting for all of it. The browser can show the page frame and fast parts first, while slow parts, such as reviews from a slow service, arrive later. The web.dev rendering guide says this can get markup to users faster and speed up first contentful paint.

Sources and further reading

Practise this with Lala

Practise