SSR en Next.js
Qué es Server Side Rendering, cómo Next.js genera HTML en el servidor, qué es el RSC Payload, cómo funciona la hidratación y cuándo elegir SSR sobre SSG o CSR.

Serie: Entendiendo Next.js — Este artículo es parte de la serie "Entendiendo Next.js", donde exploramos cómo funciona Next.js internamente.
¿Qué es Server Side Rendering?
Server Side Rendering (SSR) es una técnica de renderizado, no un tipo de componente. Consiste en que el servidor genera el HTML completo de una página y lo envía listo al navegador, en lugar de que sea el navegador quien lo construya desde cero con JavaScript.
SSR vs CSR vs SSG
Antes de profundizar, vale la pena entender las diferencias entre las tres técnicas principales de renderizado:
| Técnica | ¿Cuándo se genera el HTML? | Caso de uso típico |
|---|---|---|
| CSR (Client Side Rendering) | En el navegador, con JavaScript | Apps internas, dashboards |
| SSR (Server Side Rendering) | En el servidor, en cada request | E-commerce, contenido dinámico |
| SSG (Static Site Generation) | En build time, una sola vez | Blogs, documentación, landing pages |
SSR es la opción correcta cuando el contenido cambia frecuentemente y necesita estar actualizado en cada visita. Si el contenido es siempre el mismo para todos los usuarios, SSG es más eficiente. Si no necesitás SEO y el contenido es completamente personalizado, CSR puede ser suficiente.
¿Qué pasa cuando un usuario entra a una página con SSR?
Usuario
↓
Request al servidor
↓
Next.js ejecuta los Server Components
↓
Genera HTML + RSC Payload
↓
El navegador muestra el HTML (el usuario ya ve contenido)
↓
React hidrata los Client Components
↓
La página es interactiva
Cuando un usuario visita la página por primera vez, ocurre lo siguiente:
- El usuario hace una request (entra a la URL)
- El servidor ejecuta el código y genera el HTML completo
- El navegador recibe ese HTML y lo muestra inmediatamente
- Se descarga el JavaScript de React junto con el RSC Payload
- React realiza la hidratación, adjunta los event listeners al HTML ya existente
- La página es completamente interactiva
En navegaciones posteriores dentro de la app, el comportamiento cambia: Next.js no vuelve a pedir HTML completo al servidor. En su lugar, solicita un nuevo RSC Payload, que React usa para actualizar solo las partes necesarias de la interfaz. El servidor sigue participando, pero de forma mucho más liviana.
¿Qué es el RSC Payload?
Cuando Next.js renderiza en el servidor, no solo envía HTML. Envía dos cosas al navegador:
- HTML — para mostrar contenido al usuario de forma inmediata
- RSC Payload — una representación binaria compacta del árbol de Server Components
El RSC Payload contiene el resultado renderizado de los Server Components, referencias a los Client Components que deben ejecutarse en el cliente, y los props que se les pasan. React lo usa para reconciliar el árbol de componentes sin volver a construir el DOM desde cero.
¿Qué es la hidratación?
La hidratación es el proceso donde React toma el HTML ya renderizado por el servidor y le adjunta los event listeners y el estado de JavaScript, sin volver a construir el DOM desde cero.
Es importante entender esto: el usuario ve el contenido antes de que React termine de cargar. Primero llega el HTML visible, después React "despierta" esa interfaz y la vuelve interactiva.
¿Por qué usar SSR?
Hay dos razones principales:
SEO — los motores de búsqueda como Google reciben el HTML completo y listo para indexar. Si el contenido se genera en el cliente, los crawlers pueden no verlo correctamente.
Performance percibida — el usuario ve contenido real antes de que cargue todo el JavaScript. Esto mejora una métrica clave llamada First Contentful Paint (FCP), que mide cuánto tarda en aparecer el primer contenido en pantalla.
Ejemplo práctico: página de producto
Imaginá una tienda online. En la página de un producto:
- El título, la descripción, las imágenes y el precio se renderizan en el servidor. Llegan listos al navegador.
- El botón "Agregar al carrito" necesita manejar clicks y estado, por lo que se hidrata y ejecuta en el cliente.
SSR en Next.js App Router
En Next.js con App Router, todos los componentes son Server Components por defecto. Esto significa que se renderizan en el servidor y su JavaScript nunca llega al navegador.
// app/products/[id]/page.tsx
// Este es un Server Component — se ejecuta en el servidor
async function ProductPage({ params }: { params: Promise<{ id: string }> }) {
const { id } = await params
const product = await fetch(`https://api.example.com/products/${id}`)
.then(res => res.json())
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
<p>${product.price}</p>
<AddToCartButton productId={product.id} />
</div>
)
}
export default ProductPage
El fetch se ejecuta en el servidor. El HTML llega completo al navegador.
Para el botón interactivo, creamos un Client Component con la directiva 'use client':
// components/AddToCartButton.tsx
'use client'
import { useState } from 'react'
function AddToCartButton({ productId }: { productId: string }) {
const [added, setAdded] = useState(false)
return (
<button onClick={() => setAdded(true)}>
{added ? 'Agregado ✓' : 'Agregar al carrito'}
</button>
)
}
export default AddToCartButton
Este componente sí se hidrata en el cliente porque necesita manejar eventos e interactividad.
El límite de 'use client'
Cuando marcás un archivo con 'use client', no solo ese componente pasa a ejecutarse en el cliente, sino que todos sus imports y componentes hijos también se consideran parte del bundle del cliente. No hace falta agregar la directiva en cada archivo hijo.
Por eso, una buena práctica es agregar 'use client' lo más abajo posible en el árbol de componentes, solo donde realmente se necesita interactividad. Así minimizás el JavaScript que se envía al navegador.
// layout.tsx — Server Component
import Search from './search' // Client Component
import Logo from './logo' // Server Component
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<>
<nav>
<Logo />
<Search /> {/* Solo este componente (y sus hijos) va al cliente */}
</nav>
<main>{children}</main>
</>
)
}
Proteger código del servidor: server-only
En Next.js, es posible importar accidentalmente código del servidor en un Client Component, exponiendo API keys u otra lógica sensible. Para evitar esto, existe el paquete server-only:
npm install server-only
Importándolo en cualquier archivo, Next.js generará un error en build time si ese archivo se usa dentro de un Client Component:
// lib/data.ts
import 'server-only'
export async function getProducts() {
const res = await fetch('https://api.example.com/products', {
headers: { authorization: process.env.API_KEY },
})
return res.json()
}
Server Component vs Client Component
| Server Component | Client Component | |
|---|---|---|
| Se ejecuta en | Servidor | Servidor + Cliente |
| Hidrata en el cliente | No | Sí |
Puede usar useState, useEffect | No | Sí |
| Puede hacer fetch directo | Sí | No recomendado |
| Directiva necesaria | Ninguna | 'use client' |
Cuándo usar SSR
SSR es ideal cuando:
- El contenido cambia con frecuencia y necesita estar actualizado en cada request
- El SEO es importante, como en blogs, e-commerce o landing pages
- Querés que el usuario vea contenido real lo antes posible
Seguir leyendo
Mantente al día
Te aviso cuando publique algo nuevo. Podés darte de baja cuando quieras.


