Nuxt 3 Core Knowledge
Full Reference: See advanced.md for WebSocket composable, Nitro WebSocket handlers, Socket.IO integration, plugin pattern, SSE, and room management patterns.
Deep Knowledge: Use
mcp__documentation__fetch_docswith technology:nuxtfor comprehensive documentation.
Directory Structure
| Directory | Purpose |
|---|---|
pages/ | File-based routing |
components/ | Auto-imported components |
composables/ | Auto-imported composables |
server/ | Server routes & middleware |
layouts/ | Page layouts |
middleware/ | Route middleware |
Page Component
<script setup lang="ts">
// Auto-imports work: ref, computed, useFetch, etc.
const { data, pending } = await useFetch('/api/users')
definePageMeta({
layout: 'admin',
middleware: 'auth'
})
</script>
<template>
<div v-if="pending">Loading...</div>
<div v-else>{{ data }}</div>
</template>
Data Fetching
| Composable | Purpose |
|---|---|
useFetch | SSR-friendly fetch with caching |
useAsyncData | Custom async logic |
$fetch | Direct fetch (no SSR handling) |
useLazyFetch | Non-blocking fetch |
Server Routes
// server/api/users.get.ts
export default defineEventHandler(async (event) => {
const query = getQuery(event)
return await db.users.findMany()
})
// server/api/users.post.ts
export default defineEventHandler(async (event) => {
const body = await readBody(event)
return await db.users.create(body)
})
Key Features
- Auto-imports (components, composables, utils)
- Hybrid rendering (SSR, SSG, ISR, SPA)
- Nitro server engine
- Built-in state with
useState
When NOT to Use This Skill
This skill is for Nuxt 3 (Vue meta-framework). DO NOT use for:
- Vue without Nuxt: Use
frontend-vueskill instead - Nuxt 2: Consult KB for migration to Nuxt 3
- Next.js (React meta-framework): Use
nextjs-app-routerskill instead - Remix (React meta-framework): Use
remixskill instead - SvelteKit: Use
sveltekitskill instead - Astro: Use
astroskill instead - Vite-only setup: Use
frontend-vuewith Vite configuration
Anti-Patterns
| Anti-Pattern | Why It's Wrong | Correct Approach |
|---|---|---|
| Using $fetch instead of useFetch | No SSR handling, data not cached | Use useFetch for SSR-friendly fetching |
| Fetching in onMounted | Client-side only, no SSR | Fetch in setup with useFetch/useAsyncData |
| Not using auto-imports | Unnecessary boilerplate | Rely on auto-imports for components/composables |
| Secrets in public runtimeConfig | Exposed to client, security risk | Use server-only runtimeConfig (not public) |
| Ignoring route rules | Missed performance opportunities | Set prerender, swr, or ssr rules per route |
| No error.vue | Poor error UX | Create error.vue for global error handling |
Quick Troubleshooting
| Issue | Possible Cause | Solution |
|---|---|---|
| "useFetch is not defined" | Auto-imports not working | Check .nuxt/tsconfig.json is generated, restart dev server |
| Data not available on SSR | Using $fetch instead of useFetch | Replace $fetch with useFetch |
| "useState is not defined" | Auto-import issue | Use useState composable (Nuxt 3), ensure .nuxt is built |
| API route 404 in production | Route not generated | Check server/api/ structure, rebuild |
| Hydration mismatch | Server/client render differently | Use <ClientOnly>, ensure consistent state |
| Components not auto-imported | Not in components/ directory | Move to components/ or configure in nuxt.config |
Production Readiness
Security Configuration
// nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
// Server-only (not exposed to client)
dbPassword: process.env.DB_PASSWORD,
// Exposed to client via useRuntimeConfig()
public: {
apiBase: process.env.API_BASE,
},
},
});
Error Handling
<!-- error.vue -->
<script setup lang="ts">
import type { NuxtError } from '#app';
const props = defineProps<{ error: NuxtError }>();
const handleError = () => clearError({ redirect: '/' });
</script>
<template>
<div class="error-page">
<h1>{{ error.statusCode }}</h1>
<p>{{ error.message }}</p>
<button @click="handleError">Go Home</button>
</div>
</template>
Performance
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
'/': { prerender: true },
'/blog/**': { swr: 3600 }, // Stale-while-revalidate
'/admin/**': { ssr: false }, // Client-only
},
});
API Security
// server/api/users.post.ts
export default defineEventHandler(async (event) => {
// Validate input
const body = await readValidatedBody(event, (body) => {
return z.object({
email: z.string().email(),
name: z.string().min(2),
}).parse(body);
});
// Auth check
const session = await requireAuth(event);
return await createUser(body);
});
Testing
import { mountSuspended } from '@nuxt/test-utils/runtime';
import IndexPage from '~/pages/index.vue';
describe('Index Page', () => {
it('renders correctly', async () => {
const component = await mountSuspended(IndexPage);
expect(component.text()).toContain('Welcome');
});
});
Monitoring Metrics
| Metric | Target |
|---|---|
| Time to First Byte | < 200ms |
| First Contentful Paint | < 1.5s |
| Hydration time | < 500ms |
| API response time | < 100ms |
Checklist
- Security headers configured
- Runtime config for secrets
- Error handling (error.vue, plugins)
- Route rules (prerender, swr, ssr)
- Image optimization
- Lazy loaded components
- API validation (Zod)
- Rate limiting on API routes
- Auth middleware for protected routes
- E2E tests with @nuxt/test-utils