Concepto clave
El Streaming SSR (Server-Side Rendering con Streaming) en Next.js 15 es una evolución del SSR tradicional que permite enviar contenido al navegador en fragmentos, en lugar de esperar a que toda la página se renderice en el servidor. Imagina que estás en un restaurante: el SSR tradicional sería como esperar a que todos los platos de tu mesa estén listos antes de servirte, mientras que el Streaming SSR es como recibir la ensalada primero, luego la sopa, y finalmente el plato principal, permitiéndote empezar a comer sin demora.
Este enfoque se combina con Suspense, un componente de React que te permite "suspender" la renderización de partes de tu interfaz mientras se cargan datos o recursos. En lugar de mostrar una pantalla en blanco o un spinner global, Suspense te permite definir fallbacks (contenido de respaldo) específicos para cada sección. Por ejemplo, en una página de producto, podrías mostrar el título y la imagen inmediatamente, mientras un componente Suspense muestra "Cargando reseñas..." hasta que las reseñas se carguen desde la base de datos.
La ventaja principal es la mejora del LCP (Largest Contentful Paint) y el FCP (First Contentful Paint), métricas clave de Core Web Vitals. Al enviar HTML incrementalmente, el usuario percibe que la página carga más rápido, incluso si algunos componentes tardan más en procesarse. Esto es crucial para aplicaciones full-stack donde ciertas consultas a APIs o bases de datos pueden ser lentas.
Cómo funciona en la práctica
En Next.js 15 con el App Router, el Streaming SSR se habilita automáticamente cuando usas Server Components y Suspense. Aquí tienes un ejemplo paso a paso de una página que muestra una lista de productos:
// app/productos/page.tsx
export default function ProductosPage() {
return (
<div>
<h1>Nuestros Productos</h1>
<Suspense fallback={<p>Cargando productos destacados...</p>}>
<ProductosDestacados />
</Suspense>
<Suspense fallback={<p>Cargando todas las categorías...</p>}>
<Categorias />
</Suspense>
</div>
);
}
// app/productos/productos-destacados.tsx
async function ProductosDestacados() {
// Simula una consulta lenta a una base de datos
await new Promise(resolve => setTimeout(resolve, 2000));
const productos = await fetch('/api/productos-destacados').then(res => res.json());
return (
<ul>
{productos.map(producto => (
<li key={producto.id}>{producto.nombre}</li>
))}
</ul>
);
}
// app/productos/categorias.tsx
async function Categorias() {
const categorias = await fetch('/api/categorias').then(res => res.json());
return (
<ul>
{categorias.map(categoria => (
<li key={categoria.id}>{categoria.nombre}</li>
))}
</ul>
);
}En este código, ProductosDestacados y Categorias son Server Components que realizan fetch de datos. El navegador recibe primero el HTML con el título y los fallbacks, luego, a medida que cada componente se resuelve, Next.js envía los fragmentos de HTML actualizados. Esto se logra mediante el uso de streaming responses en el servidor, donde el contenido se escribe en el response de forma incremental.
Dato importante: Next.js 15 optimiza esto aún más con Server Components por defecto, reduciendo el JavaScript enviado al cliente y mejorando el rendimiento en dispositivos móviles.
Caso de estudio
Considera una aplicación de e-commerce construida con Next.js 15. La página de inicio necesita mostrar:
- Un banner promocional (carga instantánea desde un CDN)
- Una lista de productos recomendados (consulta a una base de datos que tarda 1.5 segundos)
- Reseñas de usuarios (consulta a una API externa que tarda 2 segundos)
- Un carrusel de categorías (datos estáticos)
Sin Streaming SSR, el usuario esperaría ~3.5 segundos antes de ver cualquier contenido. Con Streaming SSR y Suspense, la página se estructura así:
// app/page.tsx
export default function HomePage() {
return (
<>
<Banner /> {/* Componente estático */}
<Suspense fallback={<SkeletonProductos />}>
<ProductosRecomendados />
</Suspense>
<Suspense fallback={<SkeletonResenas />}>
<Resenas />
</Suspense>
<CategoriasCarrusel /> {/* Componente estático */}
</gt;
);
}Resultados medidos en un entorno de producción:
| Métrica | Sin Streaming SSR | Con Streaming SSR |
|---|---|---|
| FCP | 3.5s | 0.8s |
| LCP | 3.5s | 1.5s |
| Tiempo hasta interactivo | 4.0s | 2.0s |
El usuario ve el banner y el esqueleto de productos casi inmediatamente, mejorando la percepción de velocidad y la retención. Este caso es típico en apps full-stack donde los tiempos de respuesta de APIs varían.
Errores comunes
- Anidar Suspense innecesariamente: Evita envolver cada componente pequeño en Suspense, ya que añade overhead. Úsalo solo para bloques que cargan datos de forma independiente y con latencia significativa (>200ms).
- Olvidar fallbacks específicos: No uses un spinner genérico para todo. Diseña fallbacks que mantengan el layout (como skeletons) para evitar saltos en la interfaz. Por ejemplo, en lugar de
<p>Cargando...</p>, usa un componente<SkeletonCard />que imite la estructura del contenido final. - No manejar errores en Streaming: Si un componente suspendido falla, el usuario podría ver un fallback eterno. Combina Suspense con Error Boundaries para capturar errores y mostrar un mensaje amigable. En Next.js, puedes usar
error.tsxen el App Router para esto. - Streaming en rutas no críticas: No apliques Streaming SSR a páginas que cargan rápido por sí mismas (como páginas estáticas). Analiza el rendimiento con herramientas como Lighthouse para identificar cuellos de botella.
- Ignorar la hidratación: Recuerda que el HTML enviado por streaming debe hidratarse en el cliente. Asegúrate de que el JavaScript necesario esté optimizado (por ejemplo, usando Server Components para reducir bundle size) para evitar una hidratación lenta.
Checklist de dominio
- Puedo explicar la diferencia entre SSR tradicional y Streaming SSR en una oración simple.
- He implementado al menos un componente Suspense en un proyecto Next.js 15 con fallback personalizado.
- Sé cómo medir el impacto del Streaming SSR en Core Web Vitals usando Chrome DevTools o Lighthouse.
- Puedo identificar cuándo usar Suspense (para cargas de datos asíncronas) y cuándo no (para componentes síncronos).
- He configurado Error Boundaries junto con Suspense para manejar fallos en producción.
- Entiendo cómo Server Components en Next.js 15 reducen el JavaScript del cliente y complementan el Streaming SSR.
- Puedo optimizar un flujo de datos lento en mi aplicación usando Streaming SSR, mejorando el FCP en al menos un 30%.
Implementa Streaming SSR en una página de blog
En este ejercicio, crearás una página de blog en Next.js 15 que use Streaming SSR con Suspense para cargar contenido de forma incremental. Sigue estos pasos:
- Crea un nuevo proyecto Next.js 15 usando el App Router si no tienes uno:
npx create-next-app@latest mi-blog --typescript --app. - En
app/blog/page.tsx, define una página que muestre: un título "Últimos Artículos", una lista de artículos (que simula una carga lenta), y un sidebar con categorías (carga rápida). - Implementa un componente Server Component
ArticulosListaque usefetcha una API simulada (puedes usarsetTimeoutpara retrasar 1.5 segundos) y devuelva una lista de artículos con título y fecha. - Envuelve
ArticulosListaen un componenteSuspensecon un fallback que sea un skeleton loader (por ejemplo, divs con fondo gris). - Añade un componente
SidebarCategoriasque cargue datos estáticos o rápidos, sin Suspense. - Ejecuta la aplicación en desarrollo (
npm run dev) y abre la página /blog en el navegador. Usa la pestaña Network en DevTools para verificar que el HTML se envía en fragmentos (busca respuestas con tipo "text/html" y streaming). - Mide el FCP usando Lighthouse o DevTools y compara con una versión sin Streaming SSR (puedes comentar el Suspense temporalmente).
Entrega: Un repositorio Git con el código y un screenshot de DevTools mostrando el streaming en acción.
Pistas- Usa
async/awaiten tu Server Component para simular la carga lenta, pero recuerda que Next.js 15 maneja esto automáticamente con Streaming SSR. - Para el skeleton loader, crea un componente simple con divs que tengan altura y ancho similares a los artículos reales, usando estilos inline básicos como
style={{ backgroundColor: '#ccc', height: '100px', margin: '10px' }}. - Si no ves el streaming en DevTools, asegúrate de que estás en modo producción simulado (
npm run build && npm start) o verifica que no hay errores en la consola del servidor.
Evalua tu comprension
Completa el quiz interactivo de arriba para ganar XP.