Rendering Strategies: CSR vs SSR vs SSG vs ISR
Understanding the four main rendering strategies in modern web development and when to use each.
The Four Rendering Strategies
Modern web applications use different rendering strategies depending on their needs. Each has trade-offs in terms of performance, SEO, complexity, and user experience.
- CSR (Client-Side Rendering): JavaScript renders everything in the browser
- SSR (Server-Side Rendering): Server generates HTML on each request
- SSG (Static Site Generation): HTML generated at build time
- ISR (Incremental Static Regeneration): SSG with periodic updates
Client-Side Rendering (CSR)
Single-Page Application (SPA) - One HTML file loads once, JavaScript handles all rendering and navigation.
How CSR Works
✅ Key Point: Static files loaded once, only data fetched on navigation
Advantages
- ✓Blazing fast navigation: Only data fetched, no full page reloads
- ✓Decoupled: Frontend and backend can be developed independently
- ✓Rich interactivity: Smooth transitions, instant feedback
- ✓Extendable: Same codebase can work for mobile/desktop apps
- ✓Lower server load: Server only serves static files
Disadvantages
- ✗Poor SEO: Search engines struggle with JavaScript-rendered content
- ✗Slower initial load: Must download and parse all JavaScript first
- ✗Security risks: More vulnerable to XSS attacks
- ✗Memory usage: Heavy JavaScript can cause memory issues
- ✗Blank screen: Users see blank page until JS loads
🎯 Best For:
Highly interactive apps (dashboards, admin panels, real-time apps), authenticated users, applications where SEO isn't critical, and SPAs with rich client-side interactions.
Server-Side Rendering (SSR)
Multi-Page Application (MPA) - Each navigation generates a new HTML page on the server.
How SSR Works
⚠️ Key Point: Each navigation = New server request + New HTML page
Advantages
- ✓Better SEO: Search engines can crawl pre-rendered HTML with meta tags
- ✓Faster initial load: HTML arrives with data already populated
- ✓More secure: Better protection against XSS attacks
- ✓Scalable: Easy to add new pages without major code changes
- ✓Works without JS: Basic functionality available even if JS fails
Disadvantages
- ✗Slower navigation: Each page change requires full server round-trip
- ✗Tightly coupled: Frontend and backend code are strongly coupled
- ✗Server load: Server must generate HTML for every request
- ✗Less interactive: Full page reloads break user flow
- ✗TTFB issues: Time to First Byte can be slow on complex pages
🎯 Best For:
Applications requiring SEO (blogs, e-commerce, marketing sites), public content, first-time visitors who need fast initial load, and content-heavy sites.
Static Site Generation (SSG)
Pre-rendered at Build Time - HTML pages are generated during the build process and served as static files.
How SSG Works
✅ Key Point: HTML generated once at build time, served instantly from CDN
Advantages
- ✓Fastest performance: Pre-built HTML served from CDN
- ✓Excellent SEO: Fully rendered HTML with all content
- ✓Low server costs: No server processing needed
- ✓High availability: CDN handles traffic spikes easily
- ✓Security: No server-side vulnerabilities
Disadvantages
- ✗Build time: Must rebuild for any content changes
- ✗Not for dynamic content: Can't handle user-specific or real-time data
- ✗Long build times: Large sites can take hours to build
- ✗No personalization: Same HTML for all users
- ✗Deployment delay: Changes require rebuild and redeploy
🎯 Best For:
Blogs, documentation sites, marketing pages, portfolios, and any site with mostly static content that doesn't change frequently. Perfect for JAMstack applications.
Incremental Static Regeneration (ISR)
SSG with Periodic Updates - Pages are statically generated at build time, but can be regenerated on-demand or on a schedule.
How ISR Works
✅ Key Point: Best of both worlds - static performance with dynamic updates
Advantages
- ✓Fast performance: Static pages served from CDN
- ✓Fresh content: Pages regenerate automatically
- ✓Scalable: No server load for cached pages
- ✓On-demand revalidation: Update specific pages instantly
- ✓Great SEO: Pre-rendered HTML with up-to-date content
Disadvantages
- ✗Stale content: Users may see old content until regeneration
- ✗Complexity: More complex than pure SSG
- ✗Build time: Still need initial build
- ✗Platform dependent: Requires specific hosting (e.g., Vercel, Netlify)
- ✗Cache invalidation: Need to manage revalidation strategy
🎯 Best For:
E-commerce product pages, blog posts, documentation that updates occasionally, and sites that need static performance but with periodic content updates. Perfect for content that changes but not in real-time.
ISR Revalidation Strategies:
- Time-based: Regenerate every X seconds (e.g., 60s)
- On-demand: Trigger regeneration via API/webhook
- Hybrid: Combine both approaches
Comparison Table
| Aspect | CSR | SSR | SSG | ISR |
|---|---|---|---|---|
| Initial Load | ⚠ Slower - Must load JS first | ✓ Fast - HTML with data ready | ✓ Fastest - Pre-built HTML | ✓ Fastest - Pre-built HTML |
| Subsequent Navigation | ✓ Fastest - Only data fetched | ✗ Slower - Full page reload | ✓ Fast - Static files | ✓ Fast - Static files |
| SEO | ✗ Poor - JS-rendered content | ✓ Excellent - Pre-rendered HTML | ✓ Excellent - Pre-rendered HTML | ✓ Excellent - Pre-rendered HTML |
| Content Updates | ✓ Instant - Client-side | ✓ Instant - Server-side | ✗ Requires rebuild - Build time | ⚠ Periodic - Revalidation |
| Server Load | ✓ Low - Static files only | ✗ High - Every request | ✓ None - CDN only | ✓ Low - Only on revalidation |
| Build Time | ✓ Fast - No pre-rendering | ✓ Fast - No pre-rendering | ✗ Slow - All pages at build | ⚠ Medium - Initial build |
| Dynamic Content | ✓ Full support | ✓ Full support | ✗ Limited - Static only | ⚠ Periodic updates |
Decision Guide
Choose CSR when:
- •Building highly interactive apps (dashboards, admin panels)
- •SEO is not critical (authenticated apps, internal tools)
- •You need rich client-side interactions and real-time updates
- •Content is highly personalized per user
Choose SSR when:
- •SEO is critical (blogs, e-commerce, marketing sites)
- •Content changes frequently and needs to be fresh
- •You need user-specific content but want SEO benefits
- •First-time visitor experience is important
Choose SSG when:
- •Content is mostly static (blogs, documentation, portfolios)
- •You want the best possible performance
- •You want to minimize server costs
- •Content doesn't change frequently
Choose ISR when:
- •You want SSG performance but need periodic updates
- •Content changes but not in real-time (e-commerce products, blog posts)
- •You have thousands of pages but only some update frequently
- •You want to balance performance with content freshness
Hybrid Approaches: Combining CSR and SSR
Modern applications often combine CSR and SSR to get the best of both worlds: fast initial load with SEO benefits from SSR, and smooth client-side navigation from CSR.
The Hybrid Model: SSR + CSR Hydration
How it works: Server renders HTML with data, then JavaScript "hydrates" the page to enable client-side navigation.
The Hydration Process
- HTML arrives fully populated
- Search engines can crawl content
- Users see content immediately
- React/Next.js JavaScript downloads
- Framework initializes
- React attaches event handlers to server-rendered HTML
- Page becomes interactive
- Client-side router takes over
- Only data fetched (no full page reload)
- Smooth transitions
- Fast navigation experience
✅ Key Benefit: Best of both worlds - SEO-friendly initial load + fast client-side navigation
Implementation Patterns
1. Next.js Pages Router (getServerSideProps + Client Navigation)
// pages/products/[id].js
// Initial page load: SSR
export async function getServerSideProps({ params }) {
const product = await fetchProduct(params.id);
return { props: { product } };
}
export default function ProductPage({ product }) {
const router = useRouter();
// After hydration, Next.js router handles navigation
// Subsequent navigations use CSR (client-side routing)
return (
<div>
<h1>{product.name}</h1>
<Link href="/products/123">Next Product</Link>
{/* This link uses CSR after initial load */}
</div>
);
}What happens: First visit: Server renders HTML → Fast initial load + SEO. Click link: Client-side navigation → Only data fetched → Fast navigation.
2. Next.js App Router (Server Components + Client Components)
// app/products/[id]/page.tsx
// Server Component - Renders on server
async function ProductPage({ params }) {
const product = await fetchProduct(params.id);
return (
<div>
<ProductDetails product={product} />
<InteractiveButton /> {/* Client Component */}
</div>
);
}
// app/products/[id]/components/InteractiveButton.tsx
'use client'; // Client Component - Hydrates on client
export function InteractiveButton() {
const [count, setCount] = useState(0);
// This component hydrates on client for interactivity
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}What happens: Server Components: Render on server → No JavaScript sent → Better performance. Client Components: Hydrate on client → Enable interactivity. Navigation: Uses CSR for fast transitions.
3. Streaming SSR with Selective Hydration
// app/dashboard/page.tsx
import { Suspense } from 'react';
export default function Dashboard() {
return (
<div>
{/* Critical content streams first */}
<Header />
{/* Non-critical content loads progressively */}
<Suspense fallback={<Skeleton />}>
<UserStats /> {/* Server Component */}
</Suspense>
{/* Interactive parts hydrate separately */}
<InteractiveChart /> {/* Client Component */}
</div>
);
}Benefits: Critical content renders immediately. Non-critical content streams in. Interactive parts hydrate independently. Better perceived performance.
4. Partial Hydration (Islands Architecture)
// Only hydrate specific interactive components
function BlogPost({ post }) {
return (
<article>
{/* Static content - no hydration needed */}
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
{/* Only this component hydrates */}
<CommentSection postId={post.id} />
</article>
);
}
// CommentSection.tsx
'use client';
export function CommentSection({ postId }) {
// Only this component sends JavaScript to client
const [comments, setComments] = useState([]);
// Rest of page stays static
}Benefits: Minimal JavaScript sent to client. Faster initial load. Only interactive parts hydrate. Better performance on low-end devices.
Real-World Examples
E-commerce Product Page
// SSR for SEO + CSR for navigation
export async function getServerSideProps({ params }) {
const product = await fetchProduct(params.id);
const relatedProducts = await fetchRelatedProducts(params.id);
return { props: { product, relatedProducts } };
}
export default function ProductPage({ product, relatedProducts }) {
return (
<div>
{/* SSR content - SEO friendly */}
<ProductInfo product={product} />
{/* Client-side interactive features */}
<AddToCartButton product={product} />
<ProductCarousel products={relatedProducts} />
{/* CSR navigation */}
<Link href="/products/123">Related Product</Link>
</div>
);
}Why hybrid: Product info: SSR for SEO (search engines can index). Add to cart: CSR for instant feedback. Navigation: CSR for smooth transitions.
Social Media Feed
// Initial feed: SSR, infinite scroll: CSR
export async function getServerSideProps() {
const initialPosts = await fetchPosts({ limit: 10 });
return { props: { initialPosts } };
}
export default function Feed({ initialPosts }) {
const [posts, setPosts] = useState(initialPosts);
const loadMore = async () => {
// CSR for loading more posts
const morePosts = await fetch('/api/posts?offset=10');
setPosts([...posts, ...morePosts]);
};
return (
<div>
{/* SSR initial posts - SEO friendly */}
{posts.map(post => <PostCard key={post.id} post={post} />)}
{/* CSR for infinite scroll */}
<button onClick={loadMore}>Load More</button>
</div>
);
}Why hybrid: Initial posts: SSR for SEO and fast first paint. Load more: CSR for smooth infinite scroll. Best user experience + SEO benefits.
Performance Benefits
- ✓Fast initial load: Server-rendered HTML shows immediately
- ✓Fast navigation: Client-side routing after hydration
- ✓Progressive enhancement: Works even if JavaScript fails
- ✓Selective hydration: Only hydrate what's needed
SEO Benefits
- ✓Crawlable content: Search engines see full HTML
- ✓Meta tags: Proper Open Graph and Twitter cards
- ✓Social sharing: Rich previews work correctly
UX Benefits
- ✓No blank screen: Content visible immediately
- ✓Smooth transitions: Client-side navigation feels instant
- ✓Works offline: After initial load, app can work offline
When to Use Hybrid Approach
Use SSR + CSR hybrid when:
- •You need SEO but also want fast navigation
- •Initial page load is critical (e-commerce, blogs)
- •You have both static and dynamic content
- •You want progressive enhancement
Common patterns:
- •Public pages: SSR for SEO (homepage, product pages, blog posts)
- •App pages: CSR for speed (dashboard, admin, authenticated areas)
- •Mixed pages: SSR initial + CSR interactions (product page with reviews)
Other Hybrid Patterns
SSG + CSR
Use case: Static content with interactive features
Blog posts: SSG for performance. Comments section: CSR for real-time updates.
ISR + CSR
Use case: Periodically updated content with client interactions
Product pages: ISR for freshness. Shopping cart: CSR for instant updates.
SSR + SSG + CSR
Use case: Different strategies for different pages
Homepage: SSG (rarely changes). Product pages: ISR (periodic updates). Dashboard: CSR (authenticated, no SEO needed).
Best Practices
- Start with SSR for public pages - Get SEO benefits
- Add CSR for navigation - Use client-side routing after hydration
- Use selective hydration - Only hydrate interactive components
- Stream critical content - Use Suspense for progressive loading
- Monitor performance - Track TTFB, FCP, and hydration time
Recommendation: Most production apps use hybrid approaches. Start with one strategy, then add others where they make sense based on page requirements.