Next.js
Estrategias de rendering en Next.js
Separa ejecución, momento, entrega y reutilización para entender rendering estático, dinámico, streaming, hidratación y Cache Components en Next.js.
- Última actualización
- Actualizada
- Nivel
- Fundamentos
Next.js
Separa ejecución, momento, entrega y reutilización para entender rendering estático, dinámico, streaming, hidratación y Cache Components en Next.js.
“Rendering” en Next.js no describe una única decisión. Debes separar al menos tres ejes: dónde se ejecuta un componente, cuándo se produce su resultado y cómo se entrega al navegador. Confundirlos lleva a llamar SSR a cualquier Server Component o CSR a cualquier Client Component.
Una ruta puede combinar simultáneamente:
Ejecución
→ servidor o cliente
Momento
→ build, prerendering, request, revalidation o navegación
Entrega
→ respuesta completa, streaming o actualización RSC
Reutilización
→ sin caché, cacheada por tiempo, tags o output estáticoEjemplo:
ProductPage
├─ header estático en shell
├─ catálogo cacheado con use cache
├─ recomendaciones dinámicas bajo Suspense
└─ botón cliente hidratadoNo es correcto etiquetar toda la página únicamente como “SSR” o “SSG”.
Las clasificaciones antiguas suelen asumir una página monolítica:
SSG → se genera en build
SSR → se genera en request
CSR → se genera en browserEl App Router puede intercalar estrategias dentro de un mismo árbol. Cache Components permite prerenderizar una shell estática, incluir contenido cacheado y transmitir contenido dependiente del request.
El modelo moderno pregunta:
Su implementación se ejecuta en servidor y no entra al bundle cliente.
Puede aparecer en una ruta estática o dinámica.
Su implementación entra al grafo cliente y se hidrata.
Puede participar en HTML generado en servidor.
Server/Client
→ dónde vive la implementación
→ no determina por sí solo static/dynamicNext.js intenta producir una representación antes de una solicitud concreta:
build o regeneración
→ HTML shell + RSC payload
→ cache/CDNPuede ser completa o parcial.
Trabajo que requiere información, datos o ejecución de la petición actual:
request
→ cookies / headers / uncached data
→ render servidor
→ streamUna ruta puede contener ambos.
Con Cache Components habilitado:
// next.config.ts
const nextConfig = {
cacheComponents: true,
};Next.js intenta construir una shell. Cada región cae conceptualmente en una categoría:
Puede calcularse durante prerendering sin acceso a red, request context ni operaciones asíncronas externas.
function ProductHeading() {
return <h1>Catálogo</h1>;
}Consulta un sistema externo, pero acepta reutilizar el resultado:
import { cacheLife } from "next/cache";
async function FeaturedProducts() {
"use cache";
cacheLife("hours");
const products = await productRepository.listFeatured();
return <ProductGrid products={products} />;
}Puede incluirse en la shell y actualizarse según lifetime o invalidación.
Necesita datos frescos por request o contexto específico:
import { cookies } from "next/headers";
async function PersonalCart() {
const cookieStore = await cookies();
const cartId = cookieStore.get("cart")?.value;
const cart = await getCart(cartId);
return <CartSummary cart={cart} />;
}Debe envolverse en Suspense cuando Cache Components exige diferir esa región al request.
Cache Components produce una shell que puede contener:
static HTML
+ cached content
+ Suspense fallbacksAl llegar una petición:
shell inmediata
↓
request-specific regions execute
↓
server streams completed segmentsEsto se denomina Partial Prerendering. No necesitas decidir que toda la route sea estática o dinámica.
import { Suspense } from "react";
export default function ProductPage() {
return (
<main>
<h1>Productos</h1>
<FeaturedProducts />
<Suspense fallback={<RecommendationsSkeleton />}>
<PersonalizedRecommendations />
</Suspense>
<CartButton />
</main>
);
}Clasificación:
<h1>
→ static shell
FeaturedProducts
→ cached server content
PersonalizedRecommendations
→ dynamic server content streamed per request
CartButton
→ Client Component hydratedNext.js mantiene un modelo anterior de caching y rendering. fetch puede usar opciones como cache y next.revalidate, y route segment configs controlan comportamiento.
No mezcles ejemplos de ambos modelos sin indicar si cacheComponents está habilitado.
Con Cache Components, use cache, cacheLife, cacheTag, Suspense y request APIs forman el modelo principal.
Una representación estática se genera y reutiliza antes de cada request individual.
Aporta:
No significa:
Una región dinámica se ejecuta cuando existe request context o datos que no se reutilizan.
Aporta:
Costes:
Incremental Static Regeneration describe reutilizar contenido y regenerarlo sin reconstruir toda la aplicación.
En Cache Components se expresa mediante:
"use cache";
cacheLife("hours");
cacheTag("products");Después de una mutación:
updateTag("products");O para stale-while-revalidate:
revalidateTag("products", "max");La representación permanece cacheada bajo una política explícita.
Streaming no decide si el contenido es estático o dinámico. Decide cómo se entrega el trabajo que termina en momentos diferentes:
shell
↓
fast segment
↓
slow segmentSuspense define las fronteras de revelado.
El usuario obtiene contexto sin esperar el recurso más lento.
Headers y status pueden quedar comprometidos; múltiples fallbacks pueden fragmentar UX.
Solo las Client Components necesitan JavaScript para interacción:
HTML visible
+ RSC payload
+ client chunks
→ hydrated interactive treeUna página con mucho contenido servidor puede tener una pequeña región hidratada.
Hidratación es una fase cliente, no una estrategia de data freshness.
CSR sigue siendo útil para contenido que:
Ejemplo:
"use client";
export function DeviceOrientationWidget() {
const orientation = useDeviceOrientation();
return <Compass orientation={orientation} />;
}No uses useEffect para cargar contenido inicial que el servidor podía obtener; crea un waterfall.
const nextConfig = {
output: "export",
};Genera archivos estáticos para hosting sin servidor Next.js.
No soporta capacidades que requieren runtime:
Static export no es equivalente a una route prerenderizada dentro de un servidor Next.js. La primera no dispone del runtime después del build.
Rendering dinámico necesita un runtime.
Compatible con mayor parte del ecosistema servidor.
Puede reducir latencia en ciertos casos, pero limita APIs y paquetes.
Escala por invocación; caches en memoria pueden no persistir.
Controlas proceso y memoria, pero debes coordinar caché entre réplicas.
La estrategia de rendering debe considerar despliegue, no solo código.
| Necesidad | Estrategia probable | Trade-off |
|---|---|---|
| Contenido público casi estable | Static o use cache largo | Puede quedar obsoleto hasta invalidación |
| CMS con webhooks | Cached + tags | Necesita invalidación confiable |
| Dashboard por usuario | Dynamic bajo Suspense | Trabajo por request |
| Carrito | Dynamic + client interaction | Consistencia y mutaciones |
| Widget browser-only | Client Component | Sin HTML útil hasta cliente si SSR se deshabilita |
No existe una única etiqueta de rendering para toda la página.
Pregunta:
La caché correcta nace de esas respuestas, no de marcar toda la route como static.
La shell puede quedar obsoleta; necesitas revalidation.
Podrías compartir datos privados entre usuarios. Las request APIs no deben leerse directamente dentro de use cache; pasa únicamente valores seguros o usa una estrategia privada explícita.
Una función cacheada que necesita red puede bloquear o fallar al llenar caché. Define disponibilidad, timeouts y fallback.
Cache Components produce error de uncached data fuera de boundary. Añade Suspense o caché según intención.
Date.now() o random durante prerendering no representan cada request. Señala ejecución runtime mediante APIs apropiadas.
No optimices únicamente TTFB.
Mide:
Una shell rápida con contenido principal que tarda demasiado puede seguir siendo mala UX.
El contenido relevante puede estar en static, cached o streamed HTML. Evita cargarlo únicamente en useEffect si debe existir en la respuesta inicial.
Metadata también participa en rendering y debe resolver caché/dynamic data bajo reglas compatibles con Cache Components.
Mezcla ejecución con momento.
Puede tener HTML servidor y hydration.
Puede revalidarse.
Una route dinámica puede consumir datos cacheados.
Con Cache Components, configs como force-dynamic ya no son el modelo principal. Elimina y expresa caché/Suspense explícitamente.
Aumenta bundle y waterfall. Decide correctamente freshness.
Puede filtrar datos entre usuarios. Diseña keys y scope.
use cache a datos reutilizables.use cache expresa reutilización.Static y dynamic rendering profundiza en qué hace que una región sea prerenderizable, qué APIs requieren request y cómo se migra desde route configs antiguas.