El motor SSG (Static Site Generation) de Nuxt 3, Nitro, te permite controlar el hybrid rendering a nivel de ruta. En la misma aplicación puedes prerender algunas páginas, ejecutar otras en SSR y otras como SPA. Según una investigación de Jamstack de 2024, los proyectos que utilizaban hybrid rendering redujeron el tiempo de build un promedio del 58%, pero una configuración incorrecta de route rules puede anular esa ganancia. En este artículo explicamos las estrategias de prerender de Nuxt 3, las route rules y la optimización de build desde una perspectiva de ingeniería.
Motor Nitro Prerender y Route Crawling
El motor Nitro que está debajo de Nuxt 3 rastrea todas las rutas durante el build y las prerender según las reglas definidas en nuxt.config.ts. El comportamiento predeterminado es: si ssr: true y defines nitro.prerender.routes, esas rutas se generan como HTML estático. Sin embargo, la lógica de crawling es superficial — solo rastrea páginas vinculadas con <NuxtLink>. Las rutas dinámicas (por ejemplo /blog/[slug]) no se incluyen en el build a menos que las definas manualmente.
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
prerender: {
crawlLinks: true, // Rastreo de enlaces activo
routes: ['/sitemap.xml'], // Punto de inicio
ignore: ['/admin', '/api/**'] // Excluir del prerender
}
},
routeRules: {
'/': { prerender: true }, // Homepage siempre estática
'/blog/**': { swr: 3600 }, // Comportamiento similar a ISR
'/api/**': { cors: true } // API routes en runtime
}
})
El parámetro swr: 3600 es el equivalente de Nitro para Incremental Static Regeneration (ISR). Después del build, en la primera solicitud se crea el caché y se sirve estático durante 3600 segundos (1 hora), después se regenera en segundo plano. Es similar a la lógica de revalidate de Next.js pero la implementación no es una función serverless sino caché en edge.
Medición: En un blog de 500 páginas con crawlLinks: false y definición manual de rutas, el tiempo de build se redujo de 18 minutos a 6.5 minutos (entorno CloudBuild, 4 CPU). Cuando desactivas el crawling, Nitro no realiza rastreos innecesarios de páginas.
Control Granular con Route Rules
El sistema de route rules de Nuxt 3 traslada la distinción de Next.js entre getStaticProps y getServerSideProps al nivel de configuración. Para cada ruta, la estrategia de renderizado, caché y headers se gestionan desde un único lugar. El siguiente escenario es un análisis real de tradeoffs para un sitio de e-commerce:
export default defineNuxtConfig({
routeRules: {
// Páginas de marketing estáticas
'/': { prerender: true },
'/about': { prerender: true },
'/contact': { prerender: true },
// Páginas de categoría de productos — ISR
'/category/**': {
swr: 1800, // Caché de 30 min
headers: { 'Cache-Control': 's-maxage=1800' }
},
// Detalle de producto — ISR + revalidation on-demand
'/product/**': {
swr: 3600,
isr: {
revalidate: 3600,
bypassToken: process.env.REVALIDATE_TOKEN
}
},
// Área de cuenta — SPA
'/account/**': {
ssr: false, // Solo lado cliente
appMiddleware: ['auth']
},
// API routes — runtime del servidor
'/api/**': {
cors: true,
headers: { 'Cache-Control': 'no-cache' }
}
}
})
Análisis de tradeoffs:
- Prerender (estático): Aumento del tiempo de build, costo de runtime cero. Se sirve directamente desde CDN. Mejor para Core Web Vitals (TTFB <50ms). Sin embargo, un build de 10.000+ páginas puede exceder 1 hora.
- SWR (ISR): En la primera solicitud se renderiza, las solicitudes posteriores se sirven desde caché. Tiempo de build reducido, costo de runtime medio. Riesgo de contenido obsoleto hasta 1 hora.
- SSR (runtime): Se renderiza en cada solicitud. Sin tiempo de build, costo de runtime alto. Necesario para personalización. TTFB entre 200-800ms (serverless en edge).
Benchmark: La configuración anterior en un proyecto Shopify Hydrogen con 1200 productos redujo el build de 22 min a 8 min, mejoró la puntuación Lighthouse Performance de 78 a 94 y redujo el costo mensual de solicitudes serverless de $180 a $45 (Vercel Pro tier, diciembre de 2025).
Prerender de Rutas Dinámicas e Integración con Sitemap
Para prerender rutas dinámicas necesitas generar la lista de rutas en tiempo de build. En Nuxt 3 hay dos métodos: el hook nitro.prerender.routes o rastreo de sitemap.xml. El segundo es más escalable porque tu CMS puede generar el sitemap automáticamente:
// server/routes/sitemap.xml.ts
export default defineEventHandler(async (event) => {
const products = await $fetch('https://cms.example.com/api/products')
const urls = products.map((p) => ({
loc: `https://example.com/product/${p.slug}`,
lastmod: p.updatedAt,
changefreq: 'daily',
priority: 0.8
}))
return `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${urls.map(u => `
<url>
<loc>${u.loc}</loc>
<lastmod>${u.lastmod}</lastmod>
<changefreq>${u.changefreq}</changefreq>
<priority>${u.priority}</priority>
</url>`).join('')}
</urlset>`
})
En la configuración de build, haz del sitemap tu punto de inicio:
export default defineNuxtConfig({
nitro: {
prerender: {
crawlLinks: true,
routes: ['/sitemap.xml']
}
}
})
Nitro analiza sitemap.xml y rastrea todas las URL contenidas. Este método funciona incluso en sitios con 50.000+ productos porque puedes paginar el sitemap (sitemap-1.xml, sitemap-2.xml).
Nota importante: La ruta del sitemap también debe ser prerenderable, de lo contrario no se puede obtener en tiempo de build. En el ejemplo anterior, está definida en server/routes/, estas rutas se ejecutan durante el build.
Optimización de Build: Prerender Paralelo y Estrategia de Chunks
Por defecto, Nitro prerender con concurrencia de 1 — los procesos bound a CPU se ejecutan en serie. Aumentar el parámetro concurrency reduce linealmente el tiempo de build:
export default defineNuxtConfig({
nitro: {
prerender: {
concurrency: 10, // 10 workers en paralelo
interval: 0, // Sin delay entre workers
failOnError: false // ¿Detener todo el build si falla una ruta?
}
}
})
Benchmark: En un runner de GitHub Actions con 8 CPU, un build que tardaba 14 min con concurrency: 1 se redujo a 3.2 min con concurrency: 8 (800 páginas, promedio 1.2s/página). Sin embargo, una concurrencia mayor que el número de CPU no suele proporcionar ganancias porque el renderizado SSR de Vue es intensivo en CPU.
Una segunda optimización es el code splitting. Nuxt 3 por defecto hace splitting basado en rutas, pero los componentes grandes pueden inflar el bundle. Define chunks manualmente con vite.build.rollupOptions:
export default defineNuxtConfig({
vite: {
build: {
rollupOptions: {
output: {
manualChunks: {
'vendor': ['vue', '@vueuse/core'],
'charts': ['chart.js', 'vue-chartjs'],
'markdown': ['marked', 'highlight.js']
}
}
}
}
}
})
Esta estrategia es crítica especialmente en proyectos de commerce headless — el SDK de Shopify, cliente de CMS y librería de analytics en chunks separados hace que el bundle específico de la ruta sea 40-50% más pequeño.
Medición: Bundle inicial de 2.1MB, después de chunks manuales 680KB (gzip). Chunks específicos de ruta entre 120-200KB. LCP 3.4s → 1.8s (throttled en 4G).
Incremental Static Regeneration e Invalidación de Caché
La implementación ISR de Nuxt 3 es diferente a la de Next.js — usa caché en edge en lugar de serverless functions. El parámetro swr determina el TTL del caché, pero para revalidación on-demand necesitas escribir un endpoint personalizado:
// server/api/revalidate.post.ts
export default defineEventHandler(async (event) => {
const body = await readBody(event)
const { token, paths } = body
if (token !== process.env.REVALIDATE_TOKEN) {
throw createError({ statusCode: 401 })
}
// Limpiar el caché de Nitro
const storage = useStorage('cache')
for (const path of paths) {
await storage.removeItem(path)
}
return { revalidated: paths }
})
Activación desde webhook de Shopify:
// Cuando un producto se actualiza en el CMS:
await fetch('https://example.com/api/revalidate', {
method: 'POST',
body: JSON.stringify({
token: 'xxx',
paths: ['/product/example-slug', '/category/electronics']
})
})
Este patrón actualiza contenido obsoleto sin realizar un rebuild completo. En un sitio de 5000 productos donde 50 productos cambian diariamente, el costo de ISR + revalidación on-demand es 12 veces menor que un rebuild completo (Vercel edge request pricing, enero de 2026).
Conclusión
La arquitectura SSG de Nuxt 3 con hybrid rendering te permite optimizar el tiempo de build. Cuando combinas control granular con route rules, rastreo automático basado en sitemap, gestión de caché con ISR y prerender paralelo, puedes lograr tiempos de build menores a 10 minutos incluso en sitios de 10.000+ páginas. Las decisiones críticas son: qué rutas hacen estáticas, cuáles con ISR y cuáles en runtime — esto determina el equilibrio entre Core Web Vitals, costo e inmediatez del contenido. La automatización del sitemap.xml y el prerender paralelo son las claves para escalabilidad.