Server-Side Rendering
Improve performance and SEO with server-side rendering using Next.js, Nuxt, Astro, and other frameworks with hydration strategies.
Note: This guide follows English-language naming conventions and terminology standards common in international development teams. Examples use English identifiers and comments to maximize compatibility across codebases and tooling.
Overview
Server-side rendering (SSR) generates HTML on the server for each request, sending a fully rendered page to the browser. This improves initial page load, SEO, and social sharing previews. Modern frameworks like Next.js, Nuxt, and Astro combine SSR with client-side hydration to deliver fast first paints and interactive experiences without sacrificing crawlability.
When to Use
Use this resource when:
- Building content-heavy sites that rely on search engine indexing
- Social sharing requires accurate Open Graph previews
- Users on slow networks need meaningful content immediately
- JavaScript-heavy SPAs have poor Core Web Vitals scores
Solution
Next.js App Router with Streaming SSR
// app/page.tsx
async function getProducts() {
const res = await fetch('https://api.example.com/products', {
next: { revalidate: 60 }
});
return res.json();
}
export default async function ProductsPage() {
const products = await getProducts();
return (
<main>
<h1>Products</h1>
<ul>
{products.map(p => (
<li key={p.id}>{p.name} — ${p.price}</li>
))}
</ul>
</main>
);
}
Astro Islands Architecture
---
// Server-rendered at build time or on request
const response = await fetch('https://api.example.com/stats');
const stats = await response.json();
---
<html>
<body>
<h1>Dashboard</h1>
<!-- Static, server-rendered HTML -->
<p>Total Users: {stats.users}</p>
<!-- Interactive island hydrates on client -->
<LiveChart client:load data={stats.chart} />
</body>
</html>
Nuxt 3 SSR with Hybrid Rendering
<script setup>
const { data: posts } = await useFetch('/api/posts', {
server: true, // Render on server
default: () => []
});
</script>
<template>
<div>
<h1>Blog</h1>
<article v-for="post in posts" :key="post.id">
<h2>{{ post.title }}</h2>
<p>{{ post.excerpt }}</p>
</article>
</div>
</template>
Explanation
How hydration works:
- Server renders complete HTML and sends to browser
- Browser displays content immediately (fast LCP)
- JavaScript bundle loads and “hydrates” the page
- Event listeners attach; components become interactive
SSR vs. SSG vs. CSR:
| Strategy | Render Time | Use Case |
|---|---|---|
| SSR | Per request | Dynamic data; personalized content |
| SSG | Build time | Static content; maximum cacheability |
| CSR | Client side | Highly interactive dashboards; SPAs |
| ISR | Hybrid | News sites; product catalogs |
Variants
| Framework | Approach | Notable |
|---|---|---|
| Next.js | SSR + SSG + ISR | React; Vercel optimization |
| Nuxt | SSR + SSG | Vue; file-based routing |
| Astro | Islands | Zero JS by default; partial hydration |
| SvelteKit | SSR + CSR | Svelte; edge-ready |
| Remix | SSR + progressive enhancement | Forms work without JS |
Best Practices
- Use streaming for slow data: Suspense boundaries let critical UI render while data loads
- Avoid hydration mismatches: Server and client HTML must match exactly
- Serialize minimal state: Only pass data the client needs; avoid full database dumps
- Cache SSR responses: CDN caching with
stale-while-revalidatereduces server load - Lazy-load below-fold: Use
client:visible(Astro) or dynamic imports for non-critical interactivity
Common Mistakes
- Hydrating everything: Not every component needs to be interactive; islands architecture saves JS
- Blocking on slow APIs: A 5-second database query delays the entire page; use streaming
- Ignoring memory leaks: Each SSR request creates new component instances; clean up subscriptions
- No error boundaries: SSR crashes should return a degraded static page, not a 500
- Over-caching dynamic content: SSG caching personalized dashboards shows wrong data to wrong users
Frequently Asked Questions
Q: Does SSR hurt performance? A: It improves initial load but adds server cost. Use SSG or ISR for content that doesn’t change per user.
Q: Can I use SSR with a headless CMS? A: Yes. Fetch CMS data during SSR; the CMS only serves the API, not the rendered page.
Q: What’s the difference between SSR and hydration? A: SSR produces HTML on the server. Hydration makes that static HTML interactive on the client.
Related Resources
MVC Pattern in Modern Frontend Applications
Apply the Model-View-Controller pattern to React and Vue applications to separate data, UI, and interaction logic for maintainable component architecture
RecipeURL Encoding and Decoding: encodeURI, encodeURIComponent, and Beyond
Master URL encoding in JavaScript and other languages with encodeURI, encodeURIComponent, plus-safe handling, RFC 3986 compliance, and decoding edge cases
RecipeBuild Responsive Email Templates with MJML
Create cross-client responsive email templates using MJML markup, dynamic Handlebars variables, and inline CSS for reliable rendering across Gmail, Outlook, and Apple Mail
RecipeWebSockets for Real-Time Communication
Build bidirectional real-time communication with WebSockets, handling connection management, reconnection, and fallbacks.
RecipeSPA Performance: Code Splitting and Lazy Loading
Improve single-page application load times by splitting bundles at route and component level, implementing lazy loading with React.lazy and dynamic imports