Choosing the right rendering mode for every route
Nuxt 3 offers server side rendering, static site generation, incremental static regeneration and hybrid rendering, all in one framework. Anyone who does not know the differences between the modes gives away performance and SEO potential. This article explains all rendering modes with concrete route rule configurations and typical pitfalls.
Table of Contents
- 1. Rendering modes in Nuxt 3: overview and decision framework
- 2. SSR: server side rendering for dynamic content
- 3. SSG: static site generation for maximum performance
- 4. ISR: incremental static regeneration as a compromise
- 5. Hybrid rendering: different modes per route
- 6. Route rules: configuring rendering strategy declaratively
- 7. Understanding and avoiding hydration errors
- 8. Server caching with useFetch and useAsyncData
- 9. SSR vs. SSG vs. ISR vs. CSR in direct comparison
- 10. Summary
- 11. FAQ
1. Rendering modes in Nuxt 3: overview and decision framework
Nuxt 3 is the Vue framework with the broadest rendering spectrum: the same codebase can produce server side rendering, static site generation, incremental static regeneration and client side rendering, depending on route configuration. That makes Nuxt the foundation for practically every type of web application, but it also creates confusion when the differences between the modes are not clear. Choosing the wrong rendering mode in Nuxt can mean that an otherwise cacheable marketing page is server rendered on every single request, or that a dashboard with user-specific data is statically generated and shows identical content to every user.
The decision framework for SSR, SSG and hybrid rendering in Nuxt depends on two questions: how often does a route's data change, and is the data user-specific? Static content such as landing pages, blog posts and product pages benefits from SSG or ISR. User-specific data such as shopping carts, dashboards and profiles needs SSR or CSR. Hybrid rendering in Nuxt 3 makes it possible to make this decision route by route, without splitting the application apart.
2. SSR: server side rendering for dynamic content
Server side rendering in Nuxt means: on every HTTP request the Vue component is rendered on the server, the complete HTML is sent to the browser, and Vue then takes over reactivity in the browser through hydration. The advantage over pure client side rendering is dramatic: the browser immediately receives renderable HTML, the crawler sees complete content, and Time to First Byte and Largest Contentful Paint improve measurably. The downside: every request puts load on the server, and caching is possible at the HTTP level but is more complex than with static files.
In Nuxt 3, SSR is the default mode when ssr: true is set in nuxt.config.ts. Data fetching in the SSR context should happen exclusively via useAsyncData or useFetch, never via onMounted or reactive watchers. Why? In SSR, onMounted hooks are not executed on the server, only in the browser. Data loaded inside an onMounted hook is not present during server side rendering and produces hydration mismatches. That is one of the most common SSR mistakes in Nuxt.
// nuxt.config.ts: Global rendering mode configuration
export default defineNuxtConfig({
ssr: true, // Default: SSR for all routes
// Nitro server configuration for performance
nitro: {
compressPublicAssets: true,
// Server-side cache for API responses
routeRules: {
'/api/**': { cache: { maxAge: 60 } }, // Cache API responses for 60s
},
},
})
// pages/product/[id].vue: SSR with useAsyncData
// Data is fetched on server before HTML is sent to browser
const route = useRoute()
const { data: product, error } = await useAsyncData(
`product-${route.params.id}`, // Unique cache key
() => $fetch(`/api/products/${route.params.id}`)
)
// WRONG, only runs in browser, causes SSR mismatch:
// onMounted(() => { fetch('/api/products/' + id).then(...) })
// CORRECT, runs on server AND client, result hydrated:
// const { data } = await useFetch('/api/products/' + id)
3. SSG: static site generation for maximum performance
Static site generation in Nuxt means: at build time, all routes are rendered and output as static HTML files. No server needed, no database access at runtime, the generated files are served directly from a CDN. That produces the fastest possible Time to First Byte, since a static HTML file can be delivered from a CDN edge node in under 10ms. For content that changes rarely or never, documentation, blog posts, landing pages, SSG is the ideal choice.
Nuxt 3 with nuxi generate crawls every link in the application and renders all discovered routes. Dynamic routes have to be made known either via generateRoutes in the configuration or via the nitro:generate:init hook. The limitation of SSG: whenever content changes, a new build has to be triggered. For pages with frequently changing content, daily product price updates, current stock levels, pure SSG is impractical. This is where ISR comes into play.
4. ISR: incremental static regeneration as a compromise
Incremental Static Regeneration (ISR) in Nuxt combines the speed of static files with the freshness of SSR. A route is rendered server side on the first request, the result is cached and served directly from the cache for all subsequent requests, until the configured cache time expires. After that, the route is re-rendered on the next request and cached again. That gives a configurable trade-off between freshness and performance: a product page can be refreshed every 60 seconds without every request putting load on the server.
In Nuxt 3, ISR is configured via route rules: routeRules: { '/products/**': { isr: 60 } } states that all routes under /products/ should be cached for 60 seconds. The implementation runs through Nitro's edge cache and is compatible with most deployment platforms that support Nuxt's Nitro adapter, Vercel, Netlify, Cloudflare Pages. An important note: ISR in Nuxt 3 is not static output in the nuxi generate sense, but a server-side cache, a running Nuxt server is required.
// nuxt.config.ts: Hybrid Rendering with Route Rules
export default defineNuxtConfig({
ssr: true,
routeRules: {
// Landing pages: statically pre-rendered at build time
'/': { prerender: true },
'/about': { prerender: true },
'/blog/**': { isr: 3600 }, // Blog posts: revalidate every hour
// Product pages: ISR with 60 second revalidation
'/products/**': { isr: 60 },
// User-specific routes: SSR, no caching
'/account/**': { ssr: true },
'/checkout/**': { ssr: true },
// Dashboard: client-side only (auth required)
'/dashboard/**': { ssr: false },
// API routes: short cache, cors headers
'/api/public/**': {
cache: { maxAge: 30 },
cors: true,
headers: { 'cache-control': 's-maxage=30' },
},
'/api/user/**': { cache: false }, // Never cache user-specific API
},
})
5. Hybrid rendering: different modes per route
Hybrid rendering in Nuxt 3 is the most powerful feature of the rendering system: every route can have its own rendering mode without the application having to be split apart. A Nuxt application can simultaneously run statically generated landing pages, ISR-cached product pages, SSR-rendered account pages and CSR-only dashboards, all in one codebase, one deployment. That is the fundamental difference from frameworks that commit to a single rendering mode.
Hybrid rendering in Nuxt is based on route rules, a declarative configuration in nuxt.config.ts that specifies the rendering mode, caching strategy and further request handler options for each URL path or glob pattern. Route rules are evaluated by Nitro, the server framework underlying Nuxt. This system makes it possible to serve SEO-relevant pages statically or via ISR while always rendering pages behind authentication server side.
6. Route rules: configuring rendering strategy declaratively
Route rules in Nuxt 3 are more than just rendering mode switches. They combine rendering strategy, HTTP caching headers, redirects, proxy settings and CORS configuration in a single declarative object per route. That makes the configuration transparent and maintainable: anyone who wants to know how a specific route is rendered and cached reads the route rules instead of digging through scattered middleware files.
The most important route rule options for hybrid rendering in Nuxt: prerender: true renders the route statically at build time. isr: seconds enables incremental static regeneration with the specified revalidation time. ssr: false turns off SSR for the route and serves an empty shell that the browser fills via CSR. cache: { maxAge: seconds } configures HTTP caching without ISR. redirect: '/target' sets up server-side redirects. All options can be applied per glob pattern.
7. Understanding and avoiding hydration errors
Hydration errors in Nuxt SSR occur when the server-rendered HTML does not match the HTML that Vue would render in the browser. When loading, Vue tries to "hydrate" the existing server HTML, that is, apply reactive bindings to it without re-rendering it. If the structures do not match, errors appear in the console and, in the worst case, a broken UI that only looks correct after a client re-render. That costs performance and produces layout shifts.
The most common causes of hydration errors in Nuxt are: date values that differ between server and client, for example new Date() executed in different time zones on server and client. Math.random() calls that produce different values on server and client. Browser APIs such as localStorage, window or document that are not available on the server. The pattern to avoid this: set all browser-specific values either inside onMounted or wrap them with Nuxt's <ClientOnly> wrapper, which renders the component only in the browser.
// Hydration error: Math.random() differs between server and client
// WRONG, causes mismatch:
// const id = Math.random().toString(36).slice(2)
// CORRECT, use useId() (Vue 3.5+) or generate in onMounted:
const id = useId()
// WRONG, localStorage is not available on server:
// const theme = localStorage.getItem('theme') ?? 'light'
// CORRECT, use Nuxt's useCookie or access in onMounted:
const theme = useCookie('theme', { default: () => 'light' })
// CORRECT, ClientOnly wrapper for browser-only components:
// <ClientOnly fallback-tag="div" fallback="Loading...">
// <BrowserOnlyComponent />
// </ClientOnly>
// CORRECT, useAsyncData with server/client data parity:
const { data } = await useAsyncData('products', async () => {
// This runs on both server (SSR) and client (navigation)
// Same result ensures no hydration mismatch
return $fetch('/api/products', {
headers: useRequestHeaders(['cookie']), // Forward auth cookies
})
})
8. Server caching with useFetch and useAsyncData
In Nuxt SSR and ISR, caching is possible on several levels. The first level is Nitro's built-in route caching via route rules. The second level is the data-fetching cache of useAsyncData and useFetch: these composables cache the result of their fetch function under the given key and, on a repeated call with the same key, return the cached result without making a new network request. That prevents duplicate fetch calls during client-side navigation and SSR rendering within the same request pipeline.
The third caching level in Nuxt SSR is the Nitro server cache with cachedFunction: arbitrary asynchronous functions, database queries for example, can be cached on the Nitro server. That gives control over caching strategy at the function level: a product lookup can be cached for 60 seconds, a user record never. This granularity makes Nuxt backends considerably more capable than naive SSR implementations that run the same database query on every request.
9. SSR vs. SSG vs. ISR vs. CSR in direct comparison
| Criterion | SSR | SSG | ISR | CSR |
|---|---|---|---|---|
| Server required? | Yes | No (CDN) | Yes | No (CDN) |
| Freshness | Immediate | Only after build | Configurable | Immediate (API) |
| SEO | Very good | Very good | Very good | Poor |
| TTFB | Medium | Very fast | Fast (cache) | Slow (JS) |
| User-specific | Yes | No | No | Yes |
| Nuxt route rule | ssr: true |
prerender: true |
isr: N |
ssr: false |
The conclusion for hybrid rendering in Nuxt is clear: there is no universally best rendering mode. Static landing pages and blog posts benefit from SSG. Product pages with frequently changing prices fit perfectly with ISR at a short revalidation time. User profiles and shopping carts need SSR. Highly interactive dashboards behind authentication can run as CSR-only to take load off the server. Nuxt 3 with route rules allows all four modes to be combined within a single application.
Mironsoft
Nuxt.js SSR/SSG development, performance optimization and deployment infrastructure
Need a Nuxt application with optimal rendering strategies?
We configure SSR, SSG, ISR and hybrid rendering in Nuxt 3, with route rules, caching strategies, hydration error prevention and deployment on your target infrastructure.
Rendering audit
Analysis of existing Nuxt configurations for suboptimal rendering modes and hydration errors
Route rules configuration
Hybrid rendering strategy with ISR, SSR and static prerendering per route
Performance optimization
Server caching with cachedFunction, TTFB optimization and CDN integration
10. Summary
SSR, SSG and hybrid rendering in Nuxt 3 are not an academic concept but direct performance and SEO levers. SSR renders on the server for every request, ideal for user-specific, highly dynamic content. SSG renders at build time, ideal for static content with maximum delivery speed. ISR combines both worlds with a configurable revalidation time. CSR does not put load on the server and makes sense for auth-protected, highly interactive interfaces.
The hybrid rendering of Nuxt 3 via route rules is the decisive advantage over frameworks with a single rendering strategy. Avoiding hydration errors means: no Math.random() or new Date() without server-side consistent values, no browser API access in the SSR context without a <ClientOnly> wrapper, data fetching always via useFetch or useAsyncData instead of onMounted.
SSR, SSG and hybrid rendering in Nuxt, the essentials at a glance
SSR
Server renders on every request. Data fetching via useFetch/useAsyncData, not onMounted. Ideal for user-specific content.
SSG / ISR
SSG: static at build time. ISR: cached server side, re-rendered after a configured time. For pages with rarely to moderately changing content.
Hybrid rendering
Route rules in nuxt.config.ts: prerender, isr, ssr, cache per glob pattern. One codebase, multiple rendering modes.
Hydration
No Math.random(), new Date() or browser APIs in the SSR context. ClientOnly wrapper for browser-specific components.