Nuxt 3 Patterns
Data Fetching
useFetchdeduplicates and caches requests during SSR — use it in components, not$fetchwhich fetches twice (server + client)$fetchis for event handlers and server routes only — in<script setup>it causes hydration mismatchesuseFetchruns on server during SSR — checkprocess.serverif you need client-only data- Add
keyoption touseFetchwhen URL params change but path stays same — without it, cache returns stale data useLazyFetchdoesn't block navigation — use for non-critical data, but handle the pending state
Hydration Traps
Date.now()orMath.random()in templates cause hydration mismatches — compute once in setup or use<ClientOnly>- Browser-only APIs (localStorage, window) crash SSR — wrap in
onMountedorprocess.clientcheck - Conditional rendering based on client-only state mismatches — use
<ClientOnly>component with fallback v-ifwith async data shows flash of wrong content — usev-showor skeleton states instead
Auto-imports
- Components in
components/auto-import with folder-based naming —components/UI/Button.vuebecomes<UIButton> - Composables in
composables/must be nameduse*for auto-import —utils.tsexports won't auto-import - Server utils in
server/utils/auto-import in server routes only — not available in client code - Disable auto-imports per-file with
// @ts-nocheckor explicitly import to avoid naming collisions
Server Routes
- Files in
server/api/become API routes —server/api/users.get.tshandles GET /api/users - Method suffix (
.get.ts,.post.ts) is required for method-specific handlers — without it, handles all methods getQuery(event)for query params,readBody(event)for POST body — don't accessevent.reqdirectly- Return value is auto-serialized to JSON — throw
createError({ statusCode: 404 })for errors
State Management
useStateis SSR-safe and persists across navigation — regularref()resets on each pageuseStatekey must be unique app-wide — collisions silently share state between components- Pinia stores need
storeToRefs()to keep reactivity when destructuring — without it, values lose reactivity - Don't initialize state with browser APIs in
useStatedefault — it runs on server too
Middleware
- Global middleware in
middleware/with.global.tssuffix runs on every route — order is alphabetical - Route middleware defined in
definePageMetaruns after global — use for auth checks on specific pages navigateTo()in middleware must be returned — forgettingreturncontinues to the original route- Server middleware in
server/middleware/runs on all server requests including API routes
Configuration
runtimeConfigfor server secrets,runtimeConfig.publicfor client-safe values — env vars override withNUXT_prefixapp.config.tsfor build-time config that doesn't need env vars — it's bundled into the appnuxt.config.tschanges require restart —app.config.tschanges hot-reload
SEO and Meta
useSeoMetafor standard meta tags — type-safe and handles og:/twitter: prefixes automaticallyuseHeadfor custom tags, scripts, and links — more flexible but no type safety for meta names- Meta in
definePageMetais static — useuseSeoMetain setup for dynamic values titleTemplateinnuxt.configfor consistent titles —%s - My Sitepattern
Plugins
- Plugins run before app creation — use
nuxtApp.hook('app:created')for post-creation logic providein plugins makes values available viauseNuxtApp()— but composables are cleaner- Plugin order: numbered prefixes (
01.plugin.ts) run first, then alphabetical — dependencies need explicit ordering - Client-only plugins:
.client.tssuffix — server-only:.server.tssuffix
Build and Deploy
nuxt generatecreates static files — but API routes won't work without a servernuxt buildcreates server bundle — deploy the.outputdirectory- ISR with
routeRules:'/blog/**': { isr: 3600 }— caches pages for 1 hour - Prerender specific routes:
routeRules: { '/about': { prerender: true } }— builds static HTML at build time