El motor SSG (Static Site Generation) Nitro de Nuxt 3 te permite controlar el rendering híbrido a nivel de ruta. En la misma aplicación puedes prerender algunas páginas mientras que otras funcionan en SSR y algunas más como SPA. Según la investigación Jamstack 2024, los proyectos que utilizan rendering híbrido redujeron el tiempo de build un promedio de 58%, pero una configuración incorrecta de route rules puede anular esa ganancia. En este artículo explicamos desde una perspectiva de ingeniería las estrategias de prerender de Nuxt 3, los route rules y la optimización del build.
Motor Nitro Prerender y Route Crawling
El motor Nitro bajo Nuxt 3 analiza todas las rutas durante el build y prerrenderiza según las reglas definidas en nuxt.config.ts. El comportamiento predeterminado es: si ssr: true y nitro.prerender.routes está definido, esas rutas se producen como HTML estático. Sin embargo, la lógica de crawling es superficial — solo sigue páginas vinculadas con <NuxtLink>. Las rutas dinámicas (por ejemplo, /blog/[slug]) no entran en el build si no se definen manualmente.
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
prerender: {
crawlLinks: true, // Análisis de enlaces activo
routes: ['/sitemap.xml'], // Punto de entrada
ignore: ['/admin', '/api/**'] // Excluir del prerender
}
},
routeRules: {
'/': { prerender: true }, // Página de inicio siempre estática
'/blog/**': { swr: 3600 }, // Comportamiento similar a ISR
'/api/**': { cors: true } // Rutas API en runtime
}
})
El parámetro swr: 3600 es el equivalente de Nitro a Incremental Static Regeneration (ISR). Después del build, la primera solicitud crea un cache que se sirve estáticamente durante 3600 segundos (1 hora), luego se regenera en el trasfondo. Similar a la lógica revalidate de Next.js pero con implementación en edge cache, no en funciones serverless.
Medición: En un sitio de blog con 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 el crawling está desactivado, Nitro no realiza escaneo innecesario de páginas.
Control Granular con Route Rules
El sistema de route rules de Nuxt 3 lleva la distinción de Next.js entre getStaticProps y getServerSideProps al nivel de configuración. La estrategia de rendering, caching y headers para cada ruta se gestiona 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, // Cache 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 usuario — SPA
'/account/**': {
ssr: false, // Solo cliente
appMiddleware: ['auth']
},
// Rutas API — 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. Servido directamente desde CDN. Mejor para Core Web Vitals (TTFB <50ms). Sin embargo, el build de 10.000+ páginas puede superar 1 hora.
- SWR (ISR): Renderizado en la primera solicitud, siguientes solicitudes desde cache. Tiempo de build bajo, costo de runtime medio. Riesgo de contenido stale hasta 1 hora.
- SSR (runtime): Renderizado en cada solicitud. Sin tiempo de build, costo de runtime alto. Necesario para personalización. TTFB entre 200-800ms (serverless edge).
Benchmark: La configuración anterior se aplicó a un proyecto Hydrogen con 1200 productos. El build pasó de 22 minutos a 8 minutos, la puntuación Lighthouse Performance de 78 a 94, y el costo mensual de serverless de $180 a $45 (Vercel Pro tier, diciembre 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: hook nitro.prerender.routes o crawling de sitemap.xml. El segundo es más escalable porque tu CMS puede generar automáticamente el sitemap:
// 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, establece el sitemap como punto de entrada:
export default defineNuxtConfig({
nitro: {
prerender: {
crawlLinks: true,
routes: ['/sitemap.xml']
}
}
})
Nitro parsea sitemap.xml y analiza todas las URLs contenidas. Este enfoque funciona incluso en sitios con 50.000+ productos porque puedes paginar el sitemap (sitemap-1.xml, sitemap-2.xml).
Atención: La ruta sitemap debe prerrenderizarse, de lo contrario no puede obtenerse en tiempo de build. En el ejemplo anterior se define en server/routes/, esas rutas se ejecutan durante el build.
Optimización del Build: Prerender Paralelo y Estrategia de Chunks
Nitro prerrenderiza con concurrency 1 por defecto — las operaciones CPU bound se ejecutan en serie. Incrementar el parámetro concurrency reduce el tiempo de build de forma lineal:
export default defineNuxtConfig({
nitro: {
prerender: {
concurrency: 10, // 10 workers paralelos
interval: 0, // Sin delay entre workers
failOnError: false // ¿Detener todo si falla una ruta?
}
}
})
Benchmark: En un runner de GitHub Actions con 8 CPU, el build que tardaba 14 minutos con concurrency: 1 se redujo a 3.2 minutos con concurrency: 8 (800 páginas, promedio 1.2s por página). Sin embargo, concurrency > número de CPUs generalmente no aporta ganancia porque el renderizado SSR de Vue es CPU-intensive.
La segunda optimización es code splitting. Nuxt 3 hace splitting basado en rutas por defecto, pero componentes grandes pueden aumentar el tamaño del 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 headless commerce — si aíslas SDK de Shopify, cliente CMS y biblioteca de analytics en chunks separados, el tamaño del bundle específico de ruta se reduce 40-50%.
Medición: Bundle inicial de 2.1MB, después de manual chunks 680KB (gzip). Chunks específicos de ruta entre 120-200KB. LCP 3.4s → 1.8s (throttled a 4G).
Incremental Static Regeneration y Cache Invalidation
La implementación de ISR de Nuxt 3 es diferente a Next.js — usa edge cache en lugar de funciones serverless. El parámetro swr define el TTL del cache, pero para revalidation 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 cache de Nitro
const storage = useStorage('cache')
for (const path of paths) {
await storage.removeItem(path)
}
return { revalidated: paths }
})
Disparo desde webhook de Shopify:
// Cuando se actualiza un producto 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 stale sin ejecutar un rebuild completo. En un sitio con 5000 productos donde 50 cambian diariamente, el costo de ISR + revalidation on-demand es 12x más bajo que un rebuild completo (Vercel edge request pricing, enero 2026).
Conclusión
La arquitectura SSG de Nuxt 3 con rendering híbrido te permite optimizar el tiempo de build. Route rules ofrecen control granular, crawling basado en sitemap para prerender dinámico, e ISR para gestionar cache en runtime. Combinados, incluso sitios de 10.000+ páginas logran build en menos de 10 minutos. Las decisiones críticas son: qué rutas prerender, cuáles usar ISR y cuáles dejar en runtime — esta decisión equilibra Core Web Vitals, costo y freshness del contenido. Automatización de sitemap.xml y prerender paralelo son las claves de la escalabilidad.