Skip to content
Back to blog
Web Engineering4 September 20266 min read

RSC Streaming on Revenue Pages: What to Stream and What to Block

Which React Server Component fragments can stream on pricing and checkout, and which numbers must stay in the blocking payload.

Revenue page split into a blocking price shell and a streamed explanation slot.

Kabir Hossain

Founder, Chainweb Solutions

View profile
Next.jsReact Server ComponentsStreamingApp Router

RSC Streaming on Revenue Pages: What to Stream and What to Block

React Server Components can stream a page in pieces. That is useful on a marketing site. It is a liability on a pricing table, checkout, or signup flow if the wrong fragment arrives late.

We treat revenue pages as a different class of route. The question is not "can we stream this?" It is "which part of this page is allowed to be wrong or late for 400 ms?"

The conversion path cannot wait on a Suspense boundary

Streaming works by wrapping slow server work in Suspense and sending a fallback first. On a blog index that is fine. On a checkout summary it is not.

If price, tax, inventory, or plan entitlement streams in after the shell, users click the wrong thing or bounce when the number changes. We have watched a pricing page where the annual toggle rendered immediately and the amounts arrived 600–900 ms later. Support tickets called it a bait price.

The rule we use: anything that changes money, eligibility, or legal copy is blocking. It stays above the first Suspense boundary and is part of the initial RSC payload.

Stream the explanation, block the contract

A revenue page usually has two layers.

The contract layer is the number, the SKU, the remaining seats, the payment intent. That layer is fetched on the server with a cache policy you can explain: session-fresh, or tagged and revalidated in seconds, not minutes.

The explanation layer is feature comparison, FAQ, testimonials, and related products. That layer can stream. It can even be static. Nobody converts because a testimonial was in the first 14 kb.

On one Next.js App Router project we moved the comparison grid and FAQ into a streaming slot and kept price plus CTA in the blocked shell. Largest Contentful Paint on the CTA region dropped because we stopped waiting on the CMS for the grid. Conversion did not move until we also stopped streaming the price.

Cache tags are not a substitute for a blocking render

It is tempting to cache the whole page and revalidate on catalog change. That helps marketing pages. On checkout, a stale entitlement is a refund.

We split the cache:

  • static shell and streamed explanation: long-lived, tagged by content
  • contract fragment: no-store, or a short tagged cache that product ops can burst-invalidate

Partial Prerendering can keep the shell instant while the contract fragment remains dynamic. Use it for the chrome. Do not prerender the number you will honor in billing.

A failure mode with streamed errors

A streaming slot that fails after the shell has painted leaves a spinner or an error island next to a live Buy button. Users click anyway. The server then rejects the charge because inventory was the part that failed.

We now fail the whole revenue route if the contract fragment throws. Explanation slots may fail independently. The Buy button is not enabled until the contract fragment has resolved, even if that means a slower first paint.

Measurement that matches the page

Track time-to-correct-price, not only LCP. Time-to-correct-price is the moment the number in the CTA matches the number the charge API will use. If that gap is over 300 ms, stream less.

Also log how often a streamed slot errors while the CTA is visible. That number should be zero on checkout. A non-zero on a marketing upsell is acceptable.

Final takeaway

Stream the story. Block the price, the entitlement, and the button that spends money. If those three arrive together, the rest of the page can come later.

Related articles

Continue with articles on similar topics.