Orientation
TL;DR
Nuxt is a full-stack Vue framework that gives you SSR, SSG, SPA, ISR, and hybrid rendering out of one codebase. You pick the rendering strategy per route. Nitro is the universal server engine that compiles your server code to any deployment target. Auto-imports eliminate boilerplate. Understanding these three pillars β rendering spectrum, Nitro, and auto-imports β is the foundation for everything else in this syllabus.
Mental Model
Think of Nuxt as a compiler that takes your Vue components, server routes, and configuration, then produces an optimized full-stack application tuned for your deployment target. Itβs not βVue with extra stepsβ β itβs a different mental model where the framework owns the build pipeline, the server, and the rendering strategy.
The Rendering Spectrum
Every web page lives somewhere on this spectrum:
SSG ββββββ ISR ββββββ SSR ββββββ SPA
(build) (cached) (per-req) (client)
- SSG (Static Site Generation): HTML generated at build time. Fastest TTFB, but content is stale until you rebuild. Use for: marketing pages, docs, blogs.
- SSR (Server-Side Rendering): HTML generated on every request. Always fresh, but requires a running server. Use for: dashboards, personalized content, real-time data.
- SPA (Single Page Application): Empty HTML shell, JavaScript renders everything client-side. No server needed after deploy. Use for: internal tools, apps behind auth where SEO doesnβt matter.
- ISR (Incremental Static Regeneration): Cached SSR β serve stale while regenerating in the background. Best of both worlds for content that changes occasionally. Use for: e-commerce product pages, news articles.
- Hybrid: Mix all of the above per route in a single app. This is Nuxtβs superpower.
Nuxt defaults to universal rendering (SSR + client hydration). The server renders HTML on first load, then Vue takes over on the client for subsequent navigation. This gives you fast initial paint AND full SPA-like interactivity.
What is Nitro?
Nitro is Nuxtβs universal server engine. Itβs a separate project (nitro.build) that Nuxt uses under the hood. Hereβs why it matters:
- Write once, deploy anywhere. Nitro compiles your server code into a self-contained output for Node.js, Deno, Cloudflare Workers, Vercel Edge, Netlify, AWS Lambda β you just change a config preset.
- Not Express. Nitro uses H3, a minimal HTTP framework. If youβre expecting
req.bodyorapp.use()Express patterns, stop. H3 has its own API (getQuery,readBody,defineEventHandler). - Server routes are filesystem-based. Files in
server/api/andserver/routes/become API endpoints automatically. Same convention as pages. - Built-in features: Caching (route rules), server middleware, storage (key-value with multiple drivers), scheduled tasks.
Think of Nitro as βVite for the backendβ β it handles bundling, HMR, and output optimization for your server code.
Auto-Imports Explained
Nuxt auto-imports:
- Vue APIs:
ref,computed,watch,onMounted, etc. - Nuxt composables:
useFetch,useRoute,useRouter,useState,useHead, etc. - Your own code: anything exported from
composables/,utils/, and components fromcomponents/.
No import { ref } from 'vue' needed. The framework scans these directories and generates the import statements at build time.
How it works under the hood: Nuxt generates .nuxt/imports.d.ts with type declarations and uses unimport to inject actual imports during compilation. Your source code stays clean; the compiled output has explicit imports.
The IDE problem: Without the generated types, your editor shows red squiggles everywhere. The fix: run nuxi prepare (or npm run dev which does it automatically). This generates the .nuxt/ directory with TypeScript declarations. Your tsconfig.json extends .nuxt/tsconfig.json which adds the auto-import types.
Directory Conventions
your-app/
βββ app.vue β Root component (entry point)
βββ pages/ β File-based routing (creates vue-router config)
βββ components/ β Auto-imported Vue components
βββ composables/ β Auto-imported composable functions
βββ utils/ β Auto-imported utility functions
βββ layouts/ β Page layout wrappers
βββ middleware/ β Route navigation guards
βββ plugins/ β App-level plugins (run before render)
βββ server/
β βββ api/ β API endpoints (/api/*)
β βββ routes/ β Server routes (any path)
β βββ middleware/ β Server middleware (runs on every request)
β βββ utils/ β Server-only utilities (auto-imported in server context)
βββ public/ β Static assets (served as-is, no processing)
βββ assets/ β Processed assets (CSS, images through Vite pipeline)
βββ nuxt.config.ts β Framework configuration
βββ .nuxt/ β Generated types, build artifacts (gitignored)
Key distinction: server/ runs on Nitro (backend). Everything else runs in the Vue app (frontend, or universal during SSR). Donβt mix them up β you canβt use useFetch in a server route, and you canβt access the database directly from a component.
How to Read Official Docs
The Nuxt docs are structured in layers:
- Getting Started β linear walkthrough, read once. Covers what this chapter covers but less opinionated.
- Guide β Concepts β deep dives into specific topics (rendering, auto-imports, server). Read when you hit a chapter that covers that concept.
- API Reference β lookup table for composables, utils, components, and config options. Bookmark this; youβll use it daily.
- Examples β runnable StackBlitz demos. Useful when prose isnβt clicking.
Pro tip: The docs search is good. Use it. But the API reference pages for composables (useFetch, useAsyncData, etc.) have the most accurate type signatures and option descriptions β prefer them over blog posts or Stack Overflow.
Walkthrough
Open the 00-blank starter:
cd starters/00-blank
npm install
npm run dev
Now observe:
- Check the rendering mode. View page source in the browser β youβll see fully rendered HTML. Thatβs SSR (universal rendering) active by default.
- Open Nuxt DevTools. Press Shift+Alt+D (or click the Nuxt icon at the bottom of the page). Explore the tabs: Components, Imports, Routes, Server Routes, Storage.
- Inspect auto-imports. Look at
.nuxt/imports.d.tsβ every composable and Vue API thatβs available without explicit imports is declared there. - Trace a request. Open Network tab, navigate to the page. First request: full HTML from server. Client-side JS hydrates. Subsequent navigations: only JSON payload (no full page reload).
Gotchas
- Auto-imports confuse your IDE until
.nuxt/is generated. Runnuxi prepareif types arenβt resolving. Never commit.nuxt/to git. - βSSRβ in Nuxt means universal rendering β server renders the initial HTML, then the client hydrates and takes over. Itβs not βserver-only renderingβ like PHP/Rails. The same components run in both environments.
- Nitro routes are NOT Express. Donβt reach for
req.bodyor Express middleware patterns. H3 has its own API. If you installexpressas a dependency, youβre doing it wrong. server/vs SSR rendering. Theserver/directory contains your API/backend logic (Nitro). SSR is a rendering strategy for your Vue pages. They both run on the server, but theyβre completely separate concerns. A component rendered via SSR cannot directly call a function inserver/utils/β it must go through an HTTP call (or$fetch).- The
.nuxt/directory. Itβs generated, gitignored, and essential for TypeScript. If types break, delete it and runnuxi prepareagain.
Exercise
- Read the official Introduction page.
- Draw the rendering spectrum on paper. For each of these, choose a rendering mode and justify why:
- A company blog with 50 posts, updated weekly
- A real-time stock trading dashboard
- An internal HR tool behind corporate SSO
- A product page for an e-commerce site with 10k products
- Open the
00-blankstarter. Add a<p>{{ message }}</p>using aref()β no import statement. Verify it works. Then check.nuxt/imports.d.tsto see where the type came from.