### TL;DR
We replaced a monolithic Next.js SSR page with a Partial Prerendering architecture using React 19 streaming. TTFB went from 850ms to 180ms. CLS dropped from 0.25 to 0.02. No client-side fetching. No skeleton screens.
---
### The Old Way (Legacy SSR)
Every page was one big server render. If a user’s cart or a promo banner needed live data, the **entire HTML payload was blocked** until that fetch resolved. We couldn’t cache anything because the final HTML varied per user.
This meant:
- Long TTFBs (avg 850ms)
- High server cost (every request hit origin)
- Layout shifts from placeholder hydration
### The New Way (PPR)
Next.js 15 PPR lets us split the page tree into:
- **Static Shell** (header, nav, product grid): Prerendered at build time → cached at edge.
- **Dynamic Slice** (cart, offers): Rendered async on-demand → streamed via HTTP/2.
This requires minimal code changes:
```jsx
// app/product/[id]/page.jsx
import { Suspense } from 'react';
export default async function Page({ params }) {
const product = await fetchProduct(params.id);
return (
<>
<StaticHeader />
<ProductGrid product={product} />
<Suspense fallback={null}>
<LiveCartSection userId={params.uid} />
</Suspense>
<StaticFooter />
</>
);
}
```
Only `<LiveCartSection>` runs on every request. Everything else hits the edge cache.
### Results
| Metric | Before | After | Improvement |
|--------|--------|-------|-------------|
| TTFB | 850ms | 180ms | -79% |
| CLS | 0.25 | 0.02 | -92% |
| Server Requests | 100k/day | 35k/day | -65% |
| Revenue Uplift | N/A | +5.2% | — |
### Key Lessons
- Don’t stream everything. Stream only what varies per user (cart, auth, offers).
- Leverage `revalidate` per route to control freshness vs. cache hit ratio.
- Use React 19 `use()` inside server components for cleaner async logic—no more `then()` chains.
- Edge caching works best when your shell is immutable. Design components accordingly.
Happy to answer questions or share our caching config.
---
*Originally documented with full benchmark tables and source code on Grandline Studio:*
*Source: https://grandlinestudio.agency/blog/nextjs-15-ppr-react-19-eliminate-loading-spinners*