Choosing the Right Rendering Strategy
Knowing when to use SSG, SSR, ISR, or PPR is critical. This lesson provides a decision framework.
The Decision Tree
Does the page need request-time data?
│
├─ NO: Does data change frequently?
│ │
│ ├─ NO → SSG (Static Generation)
│ │ Build once, serve forever
│ │
│ └─ YES → ISR (Incremental Static Regeneration)
│ Build once, revalidate periodically
│
└─ YES: Does MOST of the page need request-time data?
│
├─ YES → SSR (Server-Side Rendering)
│ Render everything at request time
│
└─ NO → PPR (Partial Prerendering)
Static shell + streaming dynamic parts
Quick Reference
| Scenario | Strategy | Why |
|---|---|---|
| Marketing pages | SSG | Never changes, maximum speed |
| Blog posts | SSG or ISR | Content is stable, maybe update hourly |
| E-commerce product | ISR + on-demand | Price/stock changes trigger revalidation |
| Search results | SSR | Query is per-request |
| User dashboard | SSR | Everything is personalized |
| Product page with "Recently Viewed" | PPR | Product is static, recently viewed is dynamic |
| News homepage | ISR (60s) | Frequent updates, but not real-time |
Real-World Examples
Marketing Site
// Static at build, never changes
export default function HomePage() {
return (
<main>
<Hero />
<Features />
<Testimonials />
<Pricing />
</main>
)
}
Blog with Comments
// ISR for the post, SSR for comments
export const revalidate = 3600 // Revalidate post content hourly
export default async function BlogPost({ params }) {
const { slug } = await params
const post = await getPost(slug)
return (
<article>
<PostContent content={post.content} /> {/* Static via ISR */}
<Suspense fallback={<CommentsSkeleton />}>
<Comments postId={post.id} /> {/* Could be dynamic or ISR */}
</Suspense>
</article>
)
}
E-commerce Product
// PPR: Static product, dynamic cart/recommendations
export const experimental_ppr = true
export default async function ProductPage({ params }) {
const { id } = await params
const product = await getProduct(id)
return (
<div>
<ProductImages images={product.images} /> {/* Static */}
<ProductDetails product={product} /> {/* Static */}
<Suspense fallback={<CartButtonSkeleton />}>
<AddToCartWithStock productId={id} /> {/* Dynamic - checks stock */}
</Suspense>
<Suspense fallback={<RecsSkeleton />}>
<PersonalizedRecommendations /> {/* Dynamic */}
</Suspense>
</div>
)
}
Dashboard
// Full SSR - everything is personalized
import { cookies } from 'next/headers'
export default async function Dashboard() {
const session = await getSession()
const [stats, recentActivity, notifications] = await Promise.all([
getStats(session.userId),
getRecentActivity(session.userId),
getNotifications(session.userId)
])
return (
<div>
<StatsCards stats={stats} />
<ActivityFeed activity={recentActivity} />
<NotificationPanel notifications={notifications} />
</div>
)
}
Common Mistakes
Mistake 1: Using SSR when ISR would work
// ❌ Full SSR for semi-static content
export const dynamic = 'force-dynamic'
export default async function ProductPage() {
const product = await getProduct() // Rarely changes
return <Product data={product} />
}
// ✅ ISR with on-demand revalidation
export const revalidate = 3600
export default async function ProductPage() {
const product = await getProduct()
return <Product data={product} />
}
Mistake 2: Making entire page dynamic for small dynamic parts
// ❌ Everything dynamic because of user greeting
import { cookies } from 'next/headers'
export default async function Page() {
const user = await getUser()
return (
<div>
<h1>Hello {user.name}</h1> {/* This makes it all dynamic */}
<StaticContent /> {/* 90% of the page */}
</div>
)
}
// ✅ PPR or client-side for just the greeting
export default function Page() {
return (
<div>
<Suspense fallback={<span>Hello!</span>}>
<UserGreeting />
</Suspense>
<StaticContent />
</div>
)
}
Summary Decision Framework
- Start with SSG - it's the fastest
- Add ISR if content changes but not per-request
- Use SSR only when you need request-time data everywhere
- Consider PPR for pages with static shells and dynamic parts
- Always ask: "Does this really need to be dynamic?"

