Busca en todas las páginas de la documentación
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Estas recetas de skills están diseñadas para Claude Code, pero también funcionan con otros agentes de código con IA que admitan archivos de skill/instrucciones.
El contenido completo de SKILL.md que puedes copiar en .claude/skills/nextjs-rendering/SKILL.md:
---
name: nextjs-rendering-strategies
description: "Experiencia profunda en decisiones de SSR, SSG, ISR, CSR y Partial Prerendering para Next.js. Úsalo cuando te pregunten: estrategia de renderizado, SSR vs SSG, estático vs dinámico, PPR, partial prerendering, generateStaticParams, ISR, CSR."
allowed-tools: "Read, Write, Edit, Glob, Grep, Bash(npm:*), Bash(npx:*), Agent"
---
# Estrategias de Renderizado en Next.js
Eres un experto en renderizado de Next.js. Ayuda a los desarrolladores a elegir e implementar la estrategia de renderizado óptima para cada página.
## Diagrama de Flujo de Decisión de Renderizado
Sigue este árbol de decisiones para cada página:
1. **¿El contenido cambia por usuario o por solicitud?**
- No -> Ve al paso 2
- Sí -> Ve al paso 4
2. **¿El contenido cambia en absoluto después del build?**
- No -> **SSG** (Static Site Generation)
- Sí -> Ve al paso 3
3. **¿Con qué frecuencia cambia?**
- Intervalo predecible -> **ISR** (Incremental Static Regeneration)
- Impredecible (impulsado por eventos) -> **ISR con revalidación bajo demanda**
- Tiempo real -> **SSR** o **CSR** con polling
4. **¿La página tiene partes estáticas y dinámicas?**
- Sí -> **PPR** (Partial Prerendering) con Suspense
- No, completamente dinámica -> **SSR** (Server-Side Rendering)
- Solo interactiva en el cliente -> **CSR** (Client-Side Rendering)
## Referencia de Estrategias
### SSG - Static Site Generation
**Cuándo:** El contenido es el mismo para todos los usuarios y no cambia después del build (o cambia muy raramente).
**Ejemplos:** Páginas de marketing, documentación, entradas de blog, páginas legales.
```tsx
// app/about/page.tsx
// Esto es automáticamente estático porque no tiene funciones dinámicas
export default function AboutPage() \{
return <div>Contenido sobre nosotros...</div>;
\}
// Rutas dinámicas con generateStaticParams
// app/blog/[slug]/page.tsx
export async function generateStaticParams() \{
const posts = await getAllPosts();
return posts.map((post) => (\{ slug: post.slug \}));
\}
export default async function BlogPost(\{
params,
\}: \{
params: Promise<\{ slug: string \}>;
\}) \{
const \{ slug \} = await params;
const post = await getPost(slug);
return <Article post=\{post\} />;
\}Rendimiento: El mejor. Servido desde el borde CDN. Cero cómputo del servidor en tiempo de solicitud.
Cuándo: El contenido cambia periódicamente pero no necesita ser en tiempo real.
Ejemplos: Catálogo de productos, artículos de noticias, páginas de precios, blog con actualizaciones.
// ISR basado en tiempo
// app/products/page.tsx
export const revalidate = 3600; // revalidar cada hora
export default async function ProductsPage() \{
const products = await getProducts();
return <ProductGrid products=\{products\} />;
\}
// ISR bajo demanda vía Server Action
// app/actions.ts
"use server";
import \{ revalidatePath, revalidateTag \} from "next/cache";
export async function updateProduct(id: string, data: ProductData) \{
await db.product.update(\{ where: \{ id \}, data \});
revalidateTag("products"); // invalidar todas las páginas de productos
\}Rendimiento: Casi estático. La primera solicitud después de la revalidación provoca una reconstrucción; las solicitudes posteriores sirven la versión en caché.
Cuándo: El contenido es diferente por solicitud (específico del usuario, datos en tiempo real, autenticación).
Ejemplos: Dashboards, perfiles de usuario, carritos de compra, resultados de búsqueda.
// app/dashboard/page.tsx
import \{ cookies \} from "next/headers";
export default async function DashboardPage() \{
// Usar cookies() hace que esta ruta sea dinámica (SSR)
const cookieStore = await cookies();
const session = cookieStore.get("session");
const user = await getUser(session?.value);
const data = await getDashboardData(user.id);
return <Dashboard user=\{user\} data=\{data\} />;
\}Rendimiento: Más lento que el estático. Cada solicitud llega al servidor. Mitígalo con streaming.
Cuándo: Contenido altamente interactivo, datos en tiempo real o funciones que no necesitan SEO.
Ejemplos: Paneles de administración, herramientas de visualización de datos, editores colaborativos, mapas.
"use client";
import useSWR from "swr";
export default function LiveDashboard() \{
const \{ data, error, isLoading \} = useSWR(
"/api/metrics",
fetcher,
\{ refreshInterval: 5000 \} // polling cada 5 segundos
);
if (isLoading) return <DashboardSkeleton />;
if (error) return <ErrorMessage error=\{error\} />;
return <MetricsDisplay data=\{data\} />;
\}Rendimiento: Shell inicial más rápido (si se combina con shell SSR), pero el contenido aparece después de que cargue el JS y se completen los fetch.
Cuándo: Una página tiene contenido estático y dinámico. El shell estático debe ser instantáneo, con las partes dinámicas transmitiéndose.
Ejemplos: Páginas de producto (descripción estática, precio/stock dinámico), feeds sociales (layout estático, contenido dinámico), dashboards (navegación estática, datos dinámicos).
// next.config.ts
const config = \{ experimental: \{ ppr: true \} \};
// app/product/[id]/page.tsx
import \{ Suspense \} from "react";
export default async function ProductPage(\{
params,
\}: \{
params: Promise<\{ id: string \}>;
\}) \{
const \{ id \} = await params;
const product = await getCachedProduct(id); // estático, en caché
return (
<main>
\{/* Shell estático - prerrenderizado en tiempo de compilación */\}
<h1>\{product.name\}</h1>
<p>\{product.description\}</p>
<StaticImages images=\{product.images\} />
\{/* Agujeros dinámicos - transmitidos en tiempo de solicitud */\}
<Suspense fallback=\{<PriceSkeleton />\}>
<LivePrice productId=\{id\} />
</Suspense>
<Suspense fallback=\{<StockSkeleton />\}>
<StockStatus productId=\{id\} />
</Suspense>
<Suspense fallback=\{<ReviewsSkeleton />\}>
<Reviews productId=\{id\} />
</Suspense>
</main>
);
\}Rendimiento: Lo mejor de ambos mundos. Shell estático servido desde CDN al instante, contenido dinámico transmitido.
| Estrategia | TTFB | FCP | SEO | Actualización | Coste del servidor |
|---|---|---|---|---|---|
| SSG | El más rápido | El más rápido | Excelente | Solo en tiempo de compilación | Ninguno |
| ISR | Rápido | Rápido | Excelente | Periódica o bajo demanda | Bajo |
| SSR | Moderado | Moderado | Excelente | Tiempo real | Alto |
| CSR | Rápido (shell) | Lento (contenido) | Pobre (sin shell SSR) | Tiempo real | Bajo (solo API) |
| PPR | El más rápido (shell) | Rápido (shell) + streaming | Excelente | Híbrido | Moderado |
Estas funciones y APIs hacen que una ruta sea dinámica (SSR en lugar de SSG):
cookies() - Leer cookiesheaders() - Leer cabeceras de solicitudsearchParams - Usar parámetros de búsquedafetch(url, { cache: "no-store" }) - Fetch sin cachéexport const dynamic = "force-dynamic" - Opt-in explícito
## Ejemplo Funcional
### Ejemplo 1: El usuario pregunta "¿Esta página debería ser SSR o SSG?"
**Prompt del usuario:** "Tengo una página de detalle de producto en /products/[slug]. La información del producto cambia raramente pero el precio se actualiza varias veces al día."
**Respuesta guiada por el skill:**
La página de producto debería usar **ISR con revalidación bajo demanda** para la información del producto, combinado con **streaming** para el precio dinámico:
```tsx
// app/products/[slug]/page.tsx
import { Suspense } from "react";
export async function generateStaticParams() {
const products = await getTopProducts(100); // Pre-compilar los 100 principales
return products.map((p) => ({ slug: p.slug }));
}
export const revalidate = 86400; // Reconstruir la información del producto diariamente
export default async function ProductPage({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const product = await getProduct(slug);
return (
<main>
<h1>{product.name}</h1>
<p>{product.description}</p>
<Suspense fallback={<span>Cargando precio...</span>}>
<LivePrice slug={slug} />
</Suspense>
</main>
);
}
Además, una Server Action para revalidación bajo demanda cuando se editen productos en el CMS.
La respuesta guiada por el skill incluiría:
ppr: true en next.config.tsEste skill le da a Claude un marco de decisión estructurado:
mkdir -p .claude/skills/nextjs-rendering
# Pega el contenido de Receta en .claude/skills/nextjs-rendering/SKILL.mdcookies() en un componente profundamente anidado fuerza toda la ruta a SSR. Usa límites Suspense y PPR para aislar las partes dinámicas.experimental.ppr y puede cambiar en versiones futuras.| Enfoque | Cuándo usarlo |
|---|---|
| Astro | Sitios con mucho contenido y mínima interactividad |
| Remix | SSR completo con carga de datos anidada y mutaciones |
| Gatsby | Generación estática en tiempo de compilación con capa de datos GraphQL |
| Nuxt.js | Equivalente en Vue.js con estrategias de renderizado similares |
revalidateTag o revalidatePath activado por una Server Action o webhookcookies() -- leer cookiesheaders() -- leer cabeceras de solicitudsearchParams -- usar parámetros de búsquedafetch(url, { cache: "no-store" }) -- fetch sin cachéexport const dynamic = "force-dynamic" -- opt-in explícitogenerateStaticParams solo pre-genera los params; la estrategia de renderizado depende de lo que haga el componenteexperimental: { ppr: true } en next.config.tsexport default async function ProductPage({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
// ...
}params es una Promise y debe ser awaitedPromise.all o streaming)cookies() o headers() cuando no es necesario, forzando que toda la ruta sea dinámicarevalidate = 3600 en una página no afecta a otras páginasRevisado por Chris St. John·Última actualización: 7 jul 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥