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?
answer
- URL patterns map to behaviour
- SSR is what you get with no rule
- prerender, isr, swr, ssr keys
- double-star globs cover a subtree
- hybrid needs nuxt build, not generate
basics
~20 sIn 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.
solid answer
~40 s`routeRules` in `nuxt.config.ts` is an object from URL patterns to rules, which Nuxt hands to Nitro and applies per request. For this app I'd prerender the marketing pages (`'/': { prerender: true }`, `'/pricing': { prerender: true }`), leave `/products/**` without a rule because universal SSR is the default, give `'/blog/**'` a cache rule, `isr: 3600` on a hosting preset that implements it or `swr: 3600` when self-hosting, and set `'/dashboard/**': { ssr: false }` so the dashboard is served as a shell and rendered in the browser. `**` covers a whole subtree, and when several patterns match, their rules merge. Mixed modes need a server, so it is built with `nuxt build`; `nuxt generate` does not support hybrid rendering.
code
ts · 14 lines// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
// marketing: built once, served as static files
'/': { prerender: true },
'/pricing': { prerender: true },
'/about': { prerender: true },
// '/products/**' has no rule: SSR per request
// blog: cached and refreshed in the background
'/blog/**': { swr: 3600 },
// dashboard: shell only, rendered in the browser
'/dashboard/**': { ssr: false },
},
})go deeper
Recall that routeRules map URL patterns to prerender, ssr, swr and isr, and that SSR is what a route gets with no rule.
Explain glob patterns, how matching rules merge, why hybrid apps build with nuxt build, and how prerendered routes are discovered.
Check the build output: which routes became files, which respond with cache headers, and that no personalised route sits under a cache rule.
Own the rendering map as policy: which team may add a rule, how pages get reviewed for caching, and what a new route defaults to.
## One config, many modes By default every Nuxt 4 page gets **universal rendering**: HTML rendered on the server per request, then hydrated. **`routeRules`** in `nuxt.config.ts` changes that per URL. It is an object whose keys are path patterns and whose values are rules; Nuxt merges it into Nitro's route rules, and the Nitro server applies the matching rules to each request. A few keys (`prerender`, `redirect`, `appMiddleware`) are also read by the app in the browser. ## The plan for the four sections | Section | Pattern | Rule | What a visitor gets | |---|---|---|---| | Marketing | `'/'`, `'/pricing'`, `'/about'` | `{ prerender: true }` | HTML built during `nuxt build` and served as a static file | | Product | `'/products/**'` | none | rendered per request, the default | | Blog | `'/blog/**'` | `{ isr: 3600 }` or `{ swr: 3600 }` | rendered on demand, cached, refreshed in the background | | Dashboard | `'/dashboard/**'` | `{ ssr: false }` | an app shell; the browser renders the page | The product page needs no rule: stating `ssr: true` would only repeat the default. The blog's choice depends on where the app runs. `swr` drives Nitro's own server-side cache, while `isr` is meant for hosting presets that map it onto their platform cache. ## Patterns and merging - Keys are URL paths, not file names. `'/dashboard/**'` matches `/dashboard` and everything below it; `'/dashboard'` alone matches only that one path. - Several patterns can match one URL. Their rules are **merged**, and on a conflicting key the more specific pattern wins, so a broad `'/**'` rule can carry shared headers while narrower patterns add rendering rules. - Because rules follow URLs, renaming a page file can silently move it out from under its rule; review the patterns when routes change. ## Building and deploying a hybrid app 1. Build with **`nuxt build`**. The output contains a Nitro server plus a `.output/public/` folder holding the prerendered pages. 2. During the build, Nuxt adds static page routes whose rules say `prerender: true` to the prerender list, and Nitro renders them to files. 3. A wildcard such as `'/blog/**': { prerender: true }` does **not** discover dynamic slugs in a `nuxt build`: Nitro's link crawler is off unless `nitro.prerender.crawlLinks` is set. List such URLs in `nitro.prerender.routes`, add them in the `prerender:routes` hook, or turn crawling on. 4. At runtime the server answers everything else: SSR for products, cached renders for the blog, shells for the dashboard. `nuxt generate` is not an option here. It produces static files only, and the docs state that hybrid rendering is unavailable with it. ## Payloads for cached routes Routes with `isr` or `swr` also get a `_payload.json` generated when they are first rendered. During client-side navigation, Nuxt loads that payload for the destination instead of fetching the page's data again, so cached pages stay cheap for in-app navigation too. ## Other keys worth knowing - `redirect`: a server-side redirect for a path. - `headers` and `cors`: response headers, handy for API paths and assets. - `noScripts`: render a page without Nuxt's scripts, so it never hydrates. - `appMiddleware`: add or remove route middleware for page paths. - Per page, `defineRouteRules({ prerender: true })` sets the rule from inside the page file, behind the `experimental.inlineRouteRules` flag. ## Pitfalls - `ssr: false` on a route moves rendering into the browser; in a hybrid app the Nitro server still answers those URLs, now with the shell. - A cache rule on a page that shows per-visitor data would serve one visitor's page to another; keep personalised routes on plain SSR or client-only. - Test the build output, not the dev server: `nuxt dev` prerenders nothing, and only the production build shows what will ship. ## Checking the result After `nuxt build`, verify each section against the plan instead of trusting the config: - **Marketing:** the prerendered pages exist as HTML files under `.output/public/`, and the build log lists them as prerendered. - **Product:** each request is rendered fresh; the response has the page content but no cache headers from Nitro. - **Blog:** a `cache-control` header with `stale-while-revalidate` shows the `swr` rule is active, and a second request returns the same `etag` without a new render. - **Dashboard:** the page source shows an empty `#__nuxt` root; the content appears only once the browser has run the app. If a check fails, compare the request path with the rule's pattern first; most surprises come from a key that matches fewer URLs than intended.
- How would you prerender every blog post in this hybrid app when the posts come from app/pages/blog/[slug].vue?A `'/blog/**': { prerender: true }` rule alone won't find them in a `nuxt build`, because Nitro's link crawler is off by default there. I'd list the slugs, either in `nitro.prerender.routes` or by adding them in the `prerender:routes` hook from the CMS, or I'd set `nitro.prerender.crawlLinks: true` so links from prerendered pages are followed.
- Can a rule be declared inside the page instead of nuxt.config.ts?Yes, with `defineRouteRules({ prerender: true })` in the page's `<script setup>`, once `experimental.inlineRouteRules` is enabled. Nuxt translates it into a `routeRules` entry for that page's path. It keeps the rule next to the page, at the cost of scattering rendering policy across files.
saying these in an interview costs you the question
- Each page must set ssr: true in routeRules to be server-rendered
- A '/dashboard' key also covers /dashboard/settings
- nuxt generate supports the same hybrid route rules as nuxt build
- A prerender wildcard finds every dynamic slug automatically
- Route rules are keyed by page file names, not URLs
- An swr rule is safe on pages that show per-visitor data