---
title: "Rendering Modes"
description: "Compare Nuxt's rendering modes to Next.js, learn when each mode makes sense, and configure per-route rendering rules for the hot springs app."
canonical_url: "https://vercel.com/academy/nuxt-on-vercel/rendering-modes"
md_url: "https://vercel.com/academy/nuxt-on-vercel/rendering-modes.md"
docset_id: "vercel-academy"
doc_version: "1.0"
last_updated: "2026-05-03T01:42:22.874Z"
content_type: "lesson"
course: "nuxt-on-vercel"
course_title: "Nuxt on Vercel"
prerequisites:  []
---

<agent-instructions>
Vercel Academy — structured learning, not reference docs.
Lessons are sequenced.
Adapt commands to the human's actual environment (OS, package manager, shell, editor) — detect from project context or ask, don't assume.
The lesson shows one path; if the human's project diverges, adapt concepts to their setup.
Preserve the learning goal over literal steps.
Quizzes are pedagogical — engage, don't spoil.
Quiz answers are included for your reference.
</agent-instructions>

# Rendering Modes

# Rendering Modes

Next.js has Server Components, Client Components, static generation, ISR, and dynamic rendering. You choose per-component and per-route, and the interactions between these choices are where most of the complexity lives. "Why is this component rendering on the server when I expected it on the client?" is a rite of passage.

Nuxt defaults to universal rendering: every page renders on the server first, then hydrates on the client. You can change this globally or per-route. The mental model is simpler because there's one default and you only override it when you have a reason.

## Outcome

Understand Nuxt's rendering modes and know when to override the default.

## Fast Track

1. Review the default rendering behavior (universal/SSR)
2. Learn the `routeRules` config for per-route overrides
3. Identify which routes in the hot springs app benefit from each mode

## Hands-on exercise 5.2

Analyze the rendering modes for each route in the app and configure route rules.

**Requirements:**

1. Understand the three rendering modes: SSR (universal), CSR (client-only), and SSG (prerender)
2. Identify which routes in the hot springs app are good candidates for each mode
3. Add `routeRules` to `nuxt.config.ts` to prerender the home page
4. Understand when CSR makes sense (and why we don't use it in this app)

**Implementation hints:**

- `routeRules` in `nuxt.config.ts` configures rendering per-route at the server level
- `{ prerender: true }` generates a static HTML file at build time
- `{ ssr: false }` disables server-side rendering for a route (client-only)
- Route rules support glob patterns: `"/springs/**": { ... }` applies to all spring detail pages

Here's how the three modes compare, translated from Next.js terms:

```
Nuxt                    Next.js equivalent       When to use
────                    ──────────────────       ───────────
Universal (SSR)         Dynamic rendering        Default. Data changes per-request
Prerender (SSG)         Static generation        Content doesn't change between builds
Client-only (CSR)       "use client" + no SSR    Dashboard-style pages, auth-heavy UI
```

Our app is a good case study. Let's think about each route:

**Home page (`/`):** The content is static. No data fetching, no personalization. Prerender it. The HTML gets generated at build time and served as a static file. Fastest possible response.

**Browse page (`/springs`):** The content depends on filters, which depend on query parameters. SSR makes sense because the initial render should show all springs (good for SEO), and subsequent filter changes happen on the client.

**Detail pages (`/springs/:id`):** The spring data comes from a static JSON file. You could prerender all 17 detail pages at build time. But if the data changes (new reviews, updated descriptions), you'd need to rebuild. SSR is the safer default.

**Auth pages (`/favorites`, `/visited`, `/login`):** These are user-specific. Prerendering makes no sense because the content is different for every user. SSR works, but CSR would also work since these pages aren't crawled by search engines.

Let's configure the home page for prerendering:

```typescript title="nuxt.config.ts" {12-14}
import tailwindcss from "@tailwindcss/vite";

export default defineNuxtConfig({
  compatibilityDate: "2025-05-01",

  modules: ["nuxt-auth-utils"],

  css: ["~/assets/css/main.css"],

  vite: {
    plugins: [tailwindcss()],
  },

  routeRules: {
    "/": { prerender: true },
  },
});
```

That's it. At build time, Nuxt generates a static HTML file for `/` and serves it directly. No server-side rendering on each request. The rest of the app continues with universal rendering.

You could also prerender specific spring detail pages if you wanted:

```typescript
routeRules: {
  "/": { prerender: true },
  "/springs/breitenbush-hot-springs": { prerender: true },
  "/springs/blue-lagoon-iceland": { prerender: true },
},
```

Or all of them with a crawl:

```typescript
routeRules: {
  "/": { prerender: true },
},
nitro: {
  prerender: {
    crawlLinks: true,
  },
},
```

`crawlLinks` follows every `NuxtLink` starting from the prerendered pages and prerenders everything it finds. From the home page, it would find `/springs`, then all 17 detail pages. Powerful, but it only works for content that exists at build time.

\*\*Note: routeRules vs page-level config\*\*

`routeRules` in `nuxt.config.ts` is a server-level config. It runs before your Vue code. You can also set `definePageMeta({ ssr: false })` in a page file for client-only rendering. The difference: `routeRules` can set caching headers and other server behavior. `definePageMeta` is limited to rendering mode.

\*\*Warning: Prerendered pages don't fetch at request time\*\*

If you prerender `/springs` and the springs data changes after deployment, the page won't reflect the change until the next build. For our JSON-file-based app this doesn't matter, but it's a real consideration with databases. ISR (Incremental Static Regeneration) is available in Nuxt via `routeRules` with `swr` or `isr` options.

## Try It

1. Add the `routeRules` config to `nuxt.config.ts`
2. Run `pnpm build` and check the output. You should see `/` listed as a prerendered route
3. Run `pnpm preview` and visit the home page. Check the response headers. The page should be served as static HTML
4. Visit `/springs`. This should still be server-rendered (no prerendering)

## Commit

```bash
git add -A && git commit -m "feat(perf): add route rules to prerender home page"
```

## Done-When

- [ ] `routeRules` is configured in `nuxt.config.ts` with the home page set to prerender
- [ ] You can explain the difference between SSR, CSR, and SSG in Nuxt terms
- [ ] You can identify which rendering mode is appropriate for each route in the app
- [ ] You understand how `crawlLinks` works for prerendering linked pages

## Solution

```typescript title="nuxt.config.ts"
import tailwindcss from "@tailwindcss/vite";

export default defineNuxtConfig({
  compatibilityDate: "2025-05-01",

  modules: ["nuxt-auth-utils"],

  css: ["~/assets/css/main.css"],

  vite: {
    plugins: [tailwindcss()],
  },

  routeRules: {
    "/": { prerender: true },
  },
});
```


---

[Full course index](/academy/llms.txt) · [Sitemap](/academy/sitemap.md)
