Nuxt 3's static site generation engine, Nitro, is the first production-grade solution in the Vue ecosystem combining ISR (Incremental Static Regeneration) and route-level prerender control. In 2026, claims surfaced that SSG was dead — in reality, hybrid rendering strategies (SSG + on-demand ISR) became the most cost-effective way to optimize Core Web Vitals. Nuxt 3's routeRules API enables managing this hybrid architecture through a single configuration file.

Route-Level Rendering Strategy

In Nuxt 3, rendering mode is no longer determined at the application level but at the route level. Within nuxt.config.ts, you can define separate strategies for each route:

export default defineNuxtConfig({
  routeRules: {
    '/': { prerender: true },
    '/blog/**': { swr: 3600 },
    '/api/**': { cors: true, headers: { 'cache-control': 's-maxage=0' } },
    '/admin/**': { ssr: false },
    '/product/**': { isr: 60 }
  }
})

This structure provides these advantages: static pages (landing, blog archives) are generated at build time, while dynamic content (product pages) is prerendered on demand. The swr: 3600 setting for the /blog/** route ensures the page is served via CDN with stale-while-revalidate strategy for 1 hour — users see the cached version while revalidation is triggered in the background.

ISR vs SWR Decision

ISR (Incremental Static Regeneration) and SWR (Stale-While-Revalidate) are often conflated. ISR generates pages on demand after build and caches them, refreshing after a set duration. SWR is an HTTP cache-control header — it serves the old version while updating in the background.

Choose ISR: For infrequently updated but high-traffic pages like product catalogs or CMS content. isr: 60 = revalidate every 60 seconds.

Choose SWR: For content where freshness isn't immediately critical, like blog posts or documentation. swr: 3600 = 1-hour CDN cache + background revalidation.

At Roibase, we reduced build time by 73% with ISR (12min → 3.2min). On an e-commerce site with 15,000 product pages, we prerendered the first 500 products at build time and generated the remainder on demand via ISR.

Nitro Prerender Crawler

Nuxt 3's prerender engine, Nitro, automatically crawls internal links to generate related pages at build time. However, controlling this crawler's behavior is critical for performance:

export default defineNuxtConfig({
  nitro: {
    prerender: {
      crawlLinks: true,
      ignore: ['/admin', '/api'],
      routes: ['/sitemap.xml', '/rss.xml']
    }
  }
})

The crawlLinks: true setting carries a risk: every <a> tag in a page is crawled, which can result in unintended routes being prerendered. For example, even if social media links in the footer are external, the crawler may still visit them.

Prerender Route Whitelist

To prerender only specific routes in production, use the routes array:

nitro: {
  prerender: {
    crawlLinks: false,
    routes: async () => {
      const { data: posts } = await $fetch('/api/posts')
      return posts.map(p => `/blog/${p.slug}`)
    }
  }
}

This pattern provides fetch-based prerender control. You pull the route list from your CMS and build only those pages. On an 8,000-page headless commerce project, this approach reduced build time from 18min to 4.5min.

Bundle Splitting and Code Elimination

Nuxt 3's SSG mode does not automatically eliminate unused JavaScript in the bundle. Route-level code splitting optimizes this:

export default defineNuxtConfig({
  experimental: {
    payloadExtraction: true
  },
  router: {
    options: {
      hashMode: false,
      scrollBehaviorType: 'smooth'
    }
  }
})

The payloadExtraction: true setting extracts prerendered page data payloads into separate JSON files. This means page transitions only load diffs, reducing the initial bundle by 40%.

Tree Shaking for Unused Code Cleanup

Nuxt 3 uses auto-import, but this can result in unused components being bundled. Disable automatic scanning with components: { dirs: [] } and manually import only components you use:

export default defineNuxtConfig({
  components: false,
  imports: {
    dirs: ['composables']
  }
})

This radical approach reduced bundle size by 28% (340KB → 245KB gzip). The tradeoff: developer experience suffers — you must manually import each component. A hybrid approach: auto-import components in /components/global, manage others manually.

Hydration Strategies

SSG's largest cost is hydration — creating a Vue instance on the client side adds 200–400ms of TBT (Total Blocking Time). Nuxt 3's ssr: false disables this entirely but sacrifices SEO.

<template>
  <div>
    <ClientOnly>
      <HeavyInteractiveWidget />
    </ClientOnly>
    <StaticContent />
  </div>
</template>

The <ClientOnly> component renders only its wrapped content on the client side. In HTML generated via SSG, this section remains a placeholder; Vue skips it during hydration. With this pattern, we reduced TBT from 420ms to 180ms on a landing page hosting an analytics dashboard.

Selective Hydration

Nuxt 3.8+ includes the nuxt-island component, enabling partial hydration:

<template>
  <NuxtIsland name="ProductCard" :props="{ id: 123 }" />
</template>

NuxtIsland is server-rendered and sent to the client as HTML; hydration occurs only for this component. The rest of the page remains static. On an e-commerce site, moving product cards to islands reduced hydration cost by 64% (TBT 380ms → 135ms).

Build Performance Optimization

SSG builds exceeding 15,000 pages often exceed 20 minutes, leaving CI/CD pipelines in stale conditions. Three approaches improve Nuxt 3 build performance:

1. Parallel Prerender:

nitro: {
  prerender: {
    concurrency: 20,
    interval: 0
  }
}

concurrency: 20 renders 20 routes simultaneously. However, memory leaks are possible — runs smoothly on 32GB RAM but may trigger OOM (Out of Memory) errors on 8GB, so test on your production CI/CD server.

2. Incremental Build (Experimental):

experimental: {
  buildCache: true
}

Reads unchanged routes from cache. However, as of Nuxt 3.12, this is still beta — cache invalidation may behave incorrectly.

3. Route Chunking: Split routes into batches and build via parallel jobs:

# CI/CD pipeline
nuxt build --prerender-routes="/,/about"
nuxt build --prerender-routes="/blog/**" --append
nuxt build --prerender-routes="/product/**" --append

This approach split an 18min build into 3 parallel jobs, reducing total time to 6.5min.

Edge Deployment Considerations

When deploying SSG to Cloudflare Pages, Vercel Edge, or Netlify, consider these points:

Cloudflare Pages: The nitro.preset: 'cloudflare-pages' setting is required. ISR is unsupported; only SWR works. Cache-control must be manually configured via the _headers file.

Vercel: Natively supports ISR but vercel.json can override route-rules — configuration conflicts are possible. Use Nuxt config as the single source of truth.

Netlify: Automatically generates _redirects and _headers files, but SWR requires manual netlify.toml configuration.

At Roibase, we deploy Nuxt 3 SSG-built storefronts to Cloudflare Pages for Headless Commerce projects. Edge caching combined with ISR keeps TTFB (Time to First Byte) under 40ms and LCP (Largest Contentful Paint) around 1.2s.


Using Nuxt 3 SSG strategically means selecting the correct rendering mode for each route. By combining build-time prerender, on-demand ISR, and SWR, you can optimize Core Web Vitals while reducing build costs. Review your hydration strategies — reducing client-side JavaScript burden accounts for 60% of performance gains.