Suggest an editImprove this articleRefine the answer for “What are rendering modes in Nuxt (SSR, SSG, SPA, Hybrid)?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Nuxt rendering modes** define where and when HTML is generated. SSR renders on the server per request (best for SEO and dynamic data). SSG pre-builds pages at build time via `nuxi generate` (best for CDN speed). SPA skips the server and renders in the browser (`ssr: false`). Hybrid mixes all three per route via `routeRules` in `nuxt.config.ts`. ```typescript routeRules: { '/blog/**': { ssr: true }, '/admin/**': { ssr: false } } ``` **Key:** SSR and SSG deliver real HTML immediately. SPA ships a JS bundle the browser assembles itself.Shown above the full answer for quick recall.Answer (EN)Image**Nuxt rendering modes** control whether pages are generated on the server per request (SSR), pre-built at build time (SSG), assembled entirely in the browser (SPA), or handled differently per route (Hybrid). ## Theory ### TL;DR - SSR = a restaurant kitchen cooking fresh per order. SSG = meals pre-cooked and waiting. SPA = ingredients shipped to you to assemble at home. Hybrid = pick the approach per dish. - Main split: SSR and SSG deliver complete HTML from the server. SPA ships a JS bundle and the browser builds the page itself. - SEO + dynamic data? SSR. Static content + CDN speed? SSG. Dashboard behind login? SPA. Mixed site? Hybrid with `routeRules`. - Nuxt uses Nitro as the underlying engine for all four modes. ### Quick example ```typescript // nuxt.config.ts - the four modes in one file export default defineNuxtConfig({ // SSR (default): server renders HTML on each request ssr: true, // SSG: run `npx nuxi generate` - outputs /dist with static HTML files // ssr: true + `nuxi generate` command // SPA: browser handles everything, server ships empty shell // ssr: false // Hybrid (Nuxt 3): per-route rules routeRules: { '/blog/**': { ssr: true }, // SSR for SEO '/dashboard/**': { ssr: false } // SPA for interactivity } }) ``` Run `npx nuxi dev` with `ssr: true` and you see server logs on every page request. Switch to `ssr: false` and the logs go quiet. The browser is now doing all the work. ### Key difference SSR and SSG both produce real HTML that search crawlers and users receive immediately. The difference is timing: SSR generates that HTML at request time, SSG generates it at build time. SPA skips HTML generation entirely and ships a JS bundle, so the browser has to download, parse, and execute JavaScript before any content appears. That delay hurts SEO and perceived performance. ### When to use - **SEO + frequently updated content**: SSR. News sites, e-commerce product pages, any page where data changes hourly. - **Static content, CDN speed**: SSG. Documentation, marketing pages, landing pages. nuxt.com itself uses SSG via `nuxi generate`. - **Interactive app, no SEO requirements**: SPA. Admin panels, dashboards, anything behind authentication. - **Mixed site (blog + admin)**: Hybrid with `routeRules`. Blog routes get SSR, admin routes get SPA behavior. - **User-specific data**: SSR or Hybrid, using `useAsyncData` or `useFetch` server-side. ### Comparison table | Feature | SSR | SSG | SPA | Hybrid | |---|---|---|---|---| | **HTML delivery** | Server per request | Build-time static files | Empty shell + JS | Per-route choice | | **SEO** | Excellent | Excellent | Poor (JS-dependent) | Route-specific | | **TTFB** | Medium (server compute) | Fastest (CDN edge) | Slow (JS download + exec) | Optimized per route | | **Dynamic data** | Full (`useAsyncData`) | Build-time only | Client fetches | Mix | | **Build time** | Fast | Scales with page count | Fastest | Like SSR + static routes | | **Server cost** | High (per request) | None (CDN) | None after deploy | Low (only SSR routes) | | **Best for** | E-commerce, blogs | Docs, landing pages | Admin panels | Most real-world apps | ### How Nitro handles this Nitro, Nuxt's server engine, compiles Vue components into server renderers for SSR and SSG. For SSR, Nitro executes `useAsyncData` and `useFetch` against Node.js APIs on each incoming request, serializes the result to HTML, and injects a JSON payload into `<script>` tags for client-side hydration. For SSG, it does the same thing once at build time and writes `.html` files. SPA mode skips Nitro's renderer entirely. Vite bundles the app for the browser. Hybrid mode scans `routeRules` at build and routes SSR paths to Nitro handlers while SPA paths get a client entrypoint. The full sequence for a Hybrid SSR route like `/blog/post`: request hits Nitro, matches the `ssr: true` rule, renders Vue to an HTML string via `@vue/server-renderer`, executes `useAsyncData` in Node, injects the JSON payload into `<script>`, then streams the full HTML response. ### Common mistakes **1. Setting `ssr: false` on a site that needs SEO.** ```typescript // Wrong: search crawlers get an empty div export default defineNuxtConfig({ ssr: false }) // curl http://localhost:3000 → <div id="__nuxt"></div> // Fix: keep SSR on and verify with curl export default defineNuxtConfig({ ssr: true }) // curl http://localhost:3000 → <article>Full post content here</article> ``` **2. Using `useFetch` in SSG for external APIs that do not exist at build time.** ```typescript // Wrong: build fails because the API is not available during nuxi generate const { data } = await useFetch('/api/live-prices') // Fix: skip server execution const { data } = await useFetch('/api/live-prices', { server: false }) ``` **3. Using client-only components without `<ClientOnly>` in SSR routes.** A component that references `window` or `document` breaks server rendering. Wrap it: ```vue <ClientOnly> <HeavyChartComponent /> </ClientOnly> ``` **4. Running `nuxi build` instead of `nuxi generate` for SSG deploys.** `nuxi build` creates an SSR Node.js server. `nuxi generate` produces static HTML files for CDN. Wrong command means wrong output type and a confused deployment pipeline. **5. Expecting SSG to handle unknown dynamic params.** If you pre-generate `/users/1`, `/users/2`, `/users/3` at build time and someone requests `/users/99`, they get a 404. SSG bakes in only the pages it knows about. For all possible IDs, SSR is the right call. ### Real-world usage - **nuxt.com** uses SSG via `nuxi generate`, deployed to CDN. - **Vercel** auto-detects SSG routes from `routeRules` in Hybrid deploys. - **E-commerce sites** use SSR for cart and checkout, SSG for product listings. - **Contentful + Nuxt** setups use SSR blogs with `useAsyncData`. - **Admin dashboards** apply `ssr: false` on `/admin/**` routes. ### Follow-up questions **Q:** How does Hybrid in Nuxt 3 differ from Nuxt 2's universal mode? **A:** Nuxt 2 had a single global SSR + client hydration model. Nuxt 3 Hybrid lets you pick per route via `routeRules`, so `/blog/**` can be SSR while `/admin/**` behaves as SPA, all in one app without any global toggle. **Q:** What happens to `useState` in SSG? **A:** At build time, state initializes on the server normally. After hydration, `useState` persists in browser memory. But since there is no server at runtime, state does not survive page refreshes the way it would in SSR. **Q:** How do you simulate ISR in Nuxt? **A:** Nuxt has no native ISR like Next.js. The closest option is `routeRules` with `swr: 3600` (stale-while-revalidate), which serves cached content and revalidates in the background. For full static files plus webhook-triggered rebuilds, combine `prerender: true` with a CI trigger. **Q:** How do you debug slow SSR time-to-interactive? **A:** Profile Nitro with its dev tools, use `useLazyFetch` for non-critical data so the initial HTML arrives faster, and wrap heavy third-party widgets in `<ClientOnly>` so they do not block server rendering. **Q:** In Hybrid mode, trace the full sequence for a `/blog/post` SSR route. **A:** Request hits Nitro, matches the `ssr: true` rule in `routeRules`, renders Vue to an HTML string via `@vue/server-renderer`, executes `useAsyncData` in Node, injects the JSON payload into a `<script>` tag for hydration, then streams HTML plus payload to the client. ## Examples ### SSR blog post with server-side data fetching ```vue <!-- pages/blog/[slug].vue --> <template> <article> <h1>{{ post.title }}</h1> <div v-html="post.body" /> </article> </template> <script setup> const route = useRoute() // useAsyncData runs on the server - SEO crawlers see full content const { data: post } = await useAsyncData('post', () => $fetch(`/api/posts/${route.params.slug}`) ) </script> ``` The server executes `$fetch` against `/api/posts/[slug]` before sending HTML. View the page source and the title and body are already in the markup, not inserted later by JavaScript. ### Hybrid app: blog combined with admin panel ```typescript // nuxt.config.ts export default defineNuxtConfig({ routeRules: { '/blog/**': { ssr: true }, // Full SSR: SEO + fresh data '/dashboard/**': { ssr: false }, // SPA: no crawlers, rich interactions '/': { prerender: true } // SSG homepage: fastest possible TTFB } }) ``` ```vue <!-- pages/dashboard/analytics.vue - runs client-side only --> <script setup> const { data: stats } = await useFetch('/api/analytics', { server: false }) </script> ``` Three different behaviors in one Nuxt app. The homepage is a static file on CDN. Blog posts are SSR from a Node server. The dashboard is a SPA shell that fetches its own data after load. ### E-commerce setup with ISR-like caching ```typescript // nuxt.config.ts - realistic e-commerce routeRules export default defineNuxtConfig({ routeRules: { '/': { prerender: true }, // Static homepage '/products': { swr: 600 }, // Product list: cache 10 min, revalidate in background '/products/**': { swr: 3600 }, // Product pages: cache 1 hour '/cart': { ssr: false }, // SPA cart: user-specific, no SEO needed '/checkout': { ssr: true }, // SSR checkout: security + personalization '/admin/**': { ssr: false } // SPA admin panel } }) ``` `swr` (stale-while-revalidate) serves cached content immediately and triggers a background refresh after the cache window expires. Product pages load fast from cache while staying reasonably fresh. This is as close to Next.js ISR as Nuxt gets without external rebuild triggers.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.