skip to content

Rendering Modes

SSR, prerendering, SPA and ISR, mixed per route through routeRules. Interviewers ask you to justify a mode for one page, since TTFB, SEO and cacheability pull different ways.

on this pageshow

explore

questions

5

In Nuxt 4, what HTML does a visitor first receive under the default ssr: true, under ssr: false, and from nuxt generate?

level: juniorimportance: must knowfreq 60%

answer

  1. universal rendering is the default
  2. ssr: false sends an empty shell
  3. a loading template fills the wait
  4. generate writes into .output/public
  5. no server after generate

basics

~20 s

With Nuxt 4's default ssr: true, each request gets server-rendered HTML plus a payload for hydration. ssr: false sends a shell with an empty #__nuxt root for the browser to fill. nuxt generate prerenders pages into static HTML files at build time.

solid answer

~40 s

Nuxt 4 defaults to universal rendering (`ssr: true`): after `nuxt build`, Nitro renders each requested page on the server, so the first response already holds the content plus a serialized payload of fetched data and state, and the browser hydrates it. With `ssr: false` in `nuxt.config.ts` the response is a shell whose `<div id="__nuxt">` is empty, optionally with `app/spa-loading-template.html` shown until the app renders; all rendering and data fetching then happen in the browser. `nuxt generate`, the same as `nuxt build --prerender`, renders pages at build time: the crawler starts from `/`, follows links, and writes HTML and `_payload.json` files into `.output/public/`, plus `200.html` and `404.html` fallbacks. That output has no server, so server routes are gone.

code

ts · 12 lines
ts
// nuxt.config.ts
export default defineNuxtConfig({
  // default is true: universal rendering
  ssr: true,
  nitro: {
    prerender: {
      // for nuxt generate: pages no link reaches
      routes: ['/sitemap.xml'],
      ignore: ['/preview'],
    },
  },
})

go deeper

for a junior

Recall the three outcomes: SSR by default, an empty shell with ssr: false, and build-time HTML files from nuxt generate.

for a middle

Explain how the payload avoids refetching on hydration, how the generate crawler finds pages, and why dynamic pages need listing.

for a senior

Spell out the operational consequences: a server to run for SSR, no server routes after generate, and stale data until the next build.

for a principal

Set the app-wide default by audience and data freshness, then justify the per-route exceptions a team will add over time.

## Who renders the first HTML Every Nuxt page is a Vue component tree, and **rendering** means turning it into HTML. Nuxt 4 lets you choose where that happens for the first response, app-wide, with one config key and one command: | Setup | Where the first HTML is produced | Body of the first response | Running server needed | |---|---|---|---| | default `ssr: true`, `nuxt build` | on the server, per request | full page markup plus a serialized payload | yes | | `ssr: false`, `nuxt build` | in the browser | shell with an empty `<div id="__nuxt">` | yes, to serve the shell and server routes | | `nuxt generate` with `ssr: true` | at build time | prebuilt HTML file plus `_payload.json` | no | | `nuxt generate` with `ssr: false` | in the browser | static shell such as `index.html` | no | ## Universal rendering: the default - `ssr` defaults to `true`, which Nuxt calls **universal rendering**. - `nuxt build` produces `.output/`, including a server entry, `.output/server/index.mjs`, that you start with Node. - For each request the Nitro server runs the Vue app: route middleware, the page component and its data fetching through `useFetch` or `useAsyncData`. It returns complete HTML. - The HTML carries a **payload**, the fetched data and `useState` values, so the browser can **hydrate** the markup without fetching again. After that first load, navigation happens client-side. - The cost is a server that must run, and component code that must be safe to execute on both sides. ## ssr: false: client-side rendering - `ssr: false` in `nuxt.config.ts` turns server rendering off for the whole app. - The response is a **shell**: head tags, links to the scripts and styles, the public runtime config, and an empty `<div id="__nuxt">`. - If `app/spa-loading-template.html` exists, its markup is shown until the app mounts. Nuxt 4 places it beside `#__nuxt` rather than inside it. - All data fetching runs in the browser, crawlers see an empty body, and payload extraction is switched off. In return, browser-only APIs are usable anywhere and there is nothing to hydrate. ## nuxt generate: prerendering at build time 1. `nuxt generate` is the same as `nuxt build --prerender`: it builds for static output with Nitro's link crawler switched on. 2. The crawler loads `/`, the pages without dynamic segments, and any routes listed in `nitro.prerender.routes`, then follows the `<a href>` links it finds. 3. Each page's HTML and its `_payload.json` are written into `.output/public/`, together with `200.html` and `404.html` fallbacks for the static host. 4. You deploy `.output/public/` to any static file host. The consequences are what interviewers probe: - A page that no link reaches and no list names is **not generated**. - Data is frozen at build time; client-side navigation reuses the build-time `_payload.json` until the next build. - There is **no server** in the output, so endpoints under `server/api/` do not exist, and caching route rules such as `swr` or `isr` do not apply. - Nuxt 4 removed the top-level `generate` config key; routes to add or skip now go in `nitro.prerender.routes` and `nitro.prerender.ignore`. ## Choosing, and mixing per route These are app-wide defaults. Most real apps keep the default and mix modes per URL with `routeRules`, prerendering some pages and making others client-only, while still building with `nuxt build`. A quick guide: - same content for everyone and must be indexed: prerender; - per-request data, such as live stock or prices: universal SSR; - behind a login and browser-heavy: client-side rendering. ## Common confusions - **`ssr: false` is not serverless.** After `nuxt build` a Nitro server still ships, serving the shell and any server routes. - **`nuxt generate` is not SPA mode.** With `ssr: true` each generated file holds the page's full markup. - **Prerendered pages still hydrate.** In the browser they behave exactly like server-rendered ones. ## Seeing the difference yourself The fastest way to tell the modes apart is to look at the raw response rather than the rendered page: - **View the page source** or fetch the URL with a command-line HTTP client. Universal and prerendered pages show their headings and text inside `#__nuxt`; an `ssr: false` page shows an empty root. - **Disable JavaScript** in the browser. Server-rendered and prerendered content stays readable; a client-rendered page stays blank or shows only the loading template. - **Inspect `.output/`** after the build. `nuxt build` leaves a `server/` folder to run; `nuxt generate` leaves only `public/`, with one HTML file per generated route. These checks are what an interviewer expects when asking how you would prove which mode a page actually uses.

  • The team runs nuxt generate, and some product pages are missing from .output/public. Why?
    The generator only renders what it can find: `/`, the pages without dynamic segments, the routes in `nitro.prerender.routes`, and every internal link reached from those. A dynamic page such as `products/[id].vue` that no crawled page links to is never visited. List those URLs in `nitro.prerender.routes`, add them in the `prerender:routes` hook, or link them from a crawled page.
  • What does a static host need to serve for a generated site's unknown URLs?
    `nuxt generate` writes two fallbacks into `.output/public/`. `200.html` loads the app so the client router can render the URL, and `404.html` does the same while the host keeps a 404 status. The host must be configured to serve one of them for paths that have no file of their own.

saying these in an interview costs you the question

  • Nuxt 4 renders pages only in the browser unless SSR is enabled
  • With ssr: false, nuxt build outputs static files only
  • nuxt generate produces a client-only SPA with empty pages
  • Prerendered pages skip hydration because they are static
  • Every dynamic page is generated automatically by nuxt generate
  • Server API routes keep working after nuxt generate
open as a page

In Nuxt 4, how do routeRules let one app prerender marketing pages, server-render product pages, cache the blog and render the dashboard client-only?

level: middleimportance: must knowfreq 52%

basics

~20 s

In Nuxt 4, routeRules in nuxt.config.ts map URL patterns to behaviour: prerender: true for marketing pages, no rule for SSR product pages, isr or swr for the blog, and ssr: false for /dashboard/**. The app is built with nuxt build.

open as a page

In Nuxt 4, what does <ClientOnly> render on the server and during hydration, and when is it the right tool for a browser-only widget?

level: middleimportance: should knowfreq 42%

basics

~20 s

Nuxt 4's <ClientOnly> renders its fallback (the #fallback slot, or fallback text in a span) on the server and again during hydration, then swaps in its default slot after mounting. It suits a browser-only widget inside an otherwise server-rendered page.

open as a page

A Nuxt 4 blog on a self-hosted Node server uses routeRules isr: 3600, yet every request re-renders. Why, and would swr or prerender fit better?

level: seniorimportance: should knowfreq 38%

basics

~20 s

On a Node server, Nitro caches rendered pages only for swr or cache rules; isr is left to hosting presets with a platform cache, so alone it caches nothing. Self-hosted, use swr: 3600; prerender suits a blog rebuilt on each publish.

open as a page

In Nuxt 4, what does routeRules '/dashboard/**': { ssr: false } change for the dashboard, and what do you give up compared with SSR?

level: seniorimportance: should knowfreq 33%

basics

~20 s

With '/dashboard/**': { ssr: false }, Nuxt 4 answers dashboard URLs with an app shell: an empty #__nuxt root plus the optional loading template. Middleware, data fetching and rendering move to the browser, at the cost of indexable HTML and an early first paint.

open as a page