Nuxt and Server-Side Rendering Overview

Advanced
13 min

Nuxt and Server-Side Rendering Overview

A standard Vue application ships an empty <div id="app"> and renders everything in the browser. For marketing pages, blogs and shops that means slower first paint and content that search engines and link previews may not see. Server-side rendering (SSR) produces the HTML on the server, and Nuxt is the framework that makes SSR with Vue practical. After this lesson you will understand the rendering modes, know how Nuxt structures a project, fetch data that works on both server and client, and decide when Nuxt is the right choice.

Rendering Modes

| Mode | Where HTML is produced | Best for | | --- | --- | --- | | SPA (client-side rendering) | Browser, after JavaScript loads | Apps behind authentication, internal tools | | SSR | Server, per request; then hydrated in the browser | Content that changes per user or often | | SSG / prerendering | Build time, as static files | Docs, blogs, marketing pages | | Hybrid | Per route, any of the above | Most real sites |

Hydration is the step where the client-side Vue app attaches to server-rendered HTML, reusing the existing DOM and adding interactivity. It requires the server and client to render identical output; reading window or the current time during render causes a hydration mismatch warning.

Vue supports SSR directly via createSSRApp and renderToString from vue/server-renderer, but wiring routing, data fetching and asset handling yourself is significant work; Nuxt provides all of it.

Creating a Nuxt Project

bash
npm create nuxt@latest my-site cd my-site npm run dev

Nuxt 4 places application code in an app/ directory (app.vue as the root with <NuxtPage />, plus pages/, layouts/, components/ and composables/), server code in server/api/, and configuration in nuxt.config.ts at the project root. Routing is file-based: pages/index.vue becomes /, pages/products/[id].vue becomes /products/:id, and pages/blog/[...slug].vue catches everything under /blog. Components and composables are auto-imported, as are Vue's own APIs (ref, computed) and Nuxt's (useRoute, useFetch, navigateTo).

Fetching Data on Server and Client

Plain fetch in <script setup> would run on the server and then again in the browser. useFetch and useAsyncData run once on the server, serialize the result into the HTML payload, and reuse it during hydration:

vue
<!-- app/pages/products/[id].vue --> <script setup> const route = useRoute() const { data: product, status, error } = await useFetch(`/api/products/${route.params.id}`) useSeoMeta({ title: () => product.value?.name, description: () => product.value?.summary }) </script> <template> <p v-if="status === 'pending'">Loading...</p> <p v-else-if="error">Product not found.</p> <article v-else> <h1>{{ product.name }}</h1> </article> </template>

useSeoMeta writes <title> and meta tags into the server-rendered HTML, which is the SEO benefit in practice. The API route itself is a file in server/api/:

javascript
// server/api/products/[id].get.js export default defineEventHandler((event) => { const id = getRouterParam(event, 'id') return db.products.find(p => p.id === Number(id)) })

Nitro, Nuxt's server engine, handles these routes and deploys to Node, serverless and edge runtimes from the same code. Wrap browser-only components in <ClientOnly>.

Choosing a Mode per Route

routeRules in nuxt.config.ts sets the strategy for each path pattern:

typescript
export default defineNuxtConfig({ routeRules: { '/': { prerender: true }, // static HTML at build time '/blog/**': { swr: 3600 }, // cached on the server, revalidated hourly '/admin/**': { ssr: false } // client-only SPA section } })

npm run generate prerenders every route to static files for hosts without a Node runtime, and ssr: false globally turns Nuxt into a well-organized SPA framework.

When to Use Nuxt

Choose Nuxt when public content, SEO, social previews or first-paint speed matter, or when you want file-based routing, auto-imports and a built-in API layer. Stay with plain Vue and Vite when the app lives behind a login or you already have a backend. Everything from this course transfers unchanged; Nuxt adds conventions on top of Vue rather than replacing it.

Common mistakes

  • Accessing window, document or localStorage at the top level of <script setup>; guard with import.meta.client or move the code into onMounted.
  • Using plain fetch for initial data, causing double requests and hydration mismatches.
  • Storing per-request state in a module-level variable on the server, which leaks data between users; use useState instead.
Quick Quiz
Question 1 of 2

What is hydration?

Key Takeaways

  • SSR renders HTML on the server for faster first paint and reliable SEO; hydration then makes it interactive.
  • Nuxt provides file-based routing, auto-imports, layouts and a Nitro server layer on top of Vue.
  • useFetch/useAsyncData fetch once on the server and reuse the payload on the client; useSeoMeta sets meta tags.
  • routeRules mixes prerendering, cached SSR and client-only rendering per route.
  • Use Nuxt for public, content-driven sites; plain Vue with Vite remains ideal for apps behind a login.

Next lesson: Building a Component Library — design reusable components with fallthrough attributes, theming and a publishable build.

Nuxt and Server-Side Rendering Overview - Vue.js | CodeYourCraft | CodeYourCraft