---
trigger: glob
glob: app/*.vue
---

| No | Category | Guideline | Description | Do | Don't | Code Good | Code Bad | Severity | Docs URL |
|----|----------|-----------|-------------|----|-------|-----------|----------|----------|----------|
| 1 | Routing | Use file-based routing | Create routes by adding files in pages directory | pages/ directory with index.vue | Manual route configuration | pages/dashboard/index.vue | Custom router setup | Medium | https://nuxt.com/docs/getting-started/routing |
| 2 | Routing | Use dynamic route parameters | Create dynamic routes with bracket syntax | [id].vue for dynamic params | Hardcoded routes for dynamic content | pages/posts/[id].vue | pages/posts/post1.vue | Medium | https://nuxt.com/docs/getting-started/routing |
| 3 | Routing | Use catch-all routes | Handle multiple path segments with [...slug] | [...slug].vue for catch-all | Multiple nested dynamic routes | pages/[...slug].vue | pages/[a]/[b]/[c].vue | Low | https://nuxt.com/docs/getting-started/routing |
| 4 | Routing | Define page metadata with definePageMeta | Set page-level configuration and middleware | definePageMeta for layout middleware title | Manual route meta configuration | definePageMeta({ layout: 'admin', middleware: 'auth' }) | router.beforeEach for page config | High | https://nuxt.com/docs/api/utils/define-page-meta |
| 5 | Routing | Use validate for route params | Validate dynamic route parameters before rendering | validate function in definePageMeta | Manual validation in setup | definePageMeta({ validate: (route) => /^\d+$/.test(route.params.id) }) | if (!valid) navigateTo('/404') | Medium | https://nuxt.com/docs/api/utils/define-page-meta |
| 6 | Rendering | Use SSR by default | Server-side rendering is enabled by default | Keep ssr: true (default) | Disable SSR unnecessarily | ssr: true (default) | ssr: false for all pages | High | https://nuxt.com/docs/guide/concepts/rendering |
| 7 | Rendering | Use .client suffix for client-only components | Mark components to render only on client | ComponentName.client.vue suffix | v-if with process.client check | Comments.client.vue | <div v-if="process.client"><Comments/></div> | Medium | https://nuxt.com/docs/guide/directory-structure/components |
| 8 | Rendering | Use .server suffix for server-only components | Mark components to render only on server | ComponentName.server.vue suffix | Manual server check | HeavyMarkdown.server.vue | v-if="process.server" | Low | https://nuxt.com/docs/guide/directory-structure/components |
| 9 | DataFetching | Use useFetch for simple data fetching | Wrapper around useAsyncData for URL fetching | useFetch for API calls | $fetch in onMounted | const { data } = await useFetch('/api/posts') | onMounted(async () => { data.value = await $fetch('/api/posts') }) | High | https://nuxt.com/docs/api/composables/use-fetch |
| 10 | DataFetching | Use useAsyncData for complex fetching | Fine-grained control over async data | useAsyncData for CMS or custom fetching | useFetch for non-URL data sources | const { data } = await useAsyncData('posts', () => cms.getPosts()) | const { data } = await useFetch(() => cms.getPosts()) | Medium | https://nuxt.com/docs/api/composables/use-async-data |
| 11 | DataFetching | Use $fetch for non-reactive requests | $fetch for event handlers and non-component code | $fetch in event handlers or server routes | useFetch in click handlers | async function submit() { await $fetch('/api/submit', { method: 'POST' }) } | async function submit() { await useFetch('/api/submit') } | High | https://nuxt.com/docs/api/utils/dollarfetch |
| 12 | DataFetching | Use lazy option for non-blocking fetch | Defer data fetching for better initial load | lazy: true for below-fold content | Blocking fetch for non-critical data | useFetch('/api/comments', { lazy: true }) | await useFetch('/api/comments') for footer | Medium | https://nuxt.com/docs/api/composables/use-fetch |
| 13 | DataFetching | Use server option to control fetch location | Choose where data is fetched | server: false for client-only data | Server fetch for user-specific client data | useFetch('/api/user-preferences', { server: false }) | useFetch for localStorage-dependent data | Medium | https://nuxt.com/docs/api/composables/use-fetch |
| 14 | DataFetching | Use pick to reduce payload size | Select only needed fields from response | pick option for large responses | Fetching entire objects when few fields needed | useFetch('/api/user', { pick: ['id', 'name'] }) | useFetch('/api/user') then destructure | Low | https://nuxt.com/docs/api/composables/use-fetch |
| 15 | DataFetching | Use transform for data manipulation | Transform data before storing in state | transform option for data shaping | Manual transformation after fetch | useFetch('/api/posts', { transform: (posts) => posts.map(p => p.title) }) | const titles = data.value.map(p => p.title) | Low | https://nuxt.com/docs/api/composables/use-fetch |
| 16 | DataFetching | Handle loading and error states | Always handle pending and error states | Check status pending error refs | Ignoring loading states | <div v-if="status === 'pending'">Loading...</div> | No loading indicator | High | https://nuxt.com/docs/getting-started/data-fetching |
| 17 | Lifecycle | Avoid side effects in script setup root | Move side effects to lifecycle hooks | Side effects in onMounted | setInterval in root script setup | onMounted(() => { interval = setInterval(...) }) | <script setup>setInterval(...)</script> | High | https://nuxt.com/docs/guide/concepts/nuxt-lifecycle |
| 18 | Lifecycle | Use onMounted for DOM access | Access DOM only after component is mounted | onMounted for DOM manipulation | Direct DOM access in setup | onMounted(() => { document.getElementById('el') }) | <script setup>document.getElementById('el')</script> | High | https://nuxt.com/docs/api/composables/on-mounted |
| 19 | Lifecycle | Use nextTick for post-render access | Wait for DOM updates before accessing elements | await nextTick() after state changes | Immediate DOM access after state change | count.value++; await nextTick(); el.value.focus() | count.value++; el.value.focus() | Medium | https://nuxt.com/docs/api/utils/next-tick |
| 20 | Lifecycle | Use onPrehydrate for pre-hydration logic | Run code before Nuxt hydrates the page | onPrehydrate for client setup | onMounted for hydration-critical code | onPrehydrate(() => { console.log(window) }) | onMounted for pre-hydration needs | Low | https://nuxt.com/docs/api/composables/on-prehydrate |
| 21 | Server | Use server/api for API routes | Create API endpoints in server/api directory | server/api/users.ts for /api/users | Manual Express setup | server/api/hello.ts -> /api/hello | app.get('/api/hello') | High | https://nuxt.com/docs/guide/directory-structure/server |
| 22 | Server | Use defineEventHandler for handlers | Define server route handlers | defineEventHandler for all handlers | export default function | export default defineEventHandler((event) => { return { hello: 'world' } }) | export default function(req, res) {} | High | https://nuxt.com/docs/guide/directory-structure/server |
| 23 | Server | Use server/routes for non-api routes | Routes without /api prefix | server/routes for custom paths | server/api for non-api routes | server/routes/sitemap.xml.ts | server/api/sitemap.xml.ts | Medium | https://nuxt.com/docs/guide/directory-structure/server |
| 24 | Server | Use getQuery and readBody for input | Access query params and request body | getQuery(event) readBody(event) | Direct event access | const { id } = getQuery(event) | event.node.req.query | Medium | https://nuxt.com/docs/guide/directory-structure/server |
| 25 | Server | Validate server input | Always validate input in server handlers | Zod or similar for validation | Trust client input | const body = await readBody(event); schema.parse(body) | const body = await readBody(event) | High | https://nuxt.com/docs/guide/directory-structure/server |
| 26 | State | Use useState for shared reactive state | SSR-friendly shared state across components | useState for cross-component state | ref for shared state | const count = useState('count', () => 0) | const count = ref(0) in composable | High | https://nuxt.com/docs/api/composables/use-state |
| 27 | State | Use unique keys for useState | Prevent state conflicts with unique keys | Descriptive unique keys for each state | Generic or duplicate keys | useState('user-preferences', () => ({})) | useState('data') in multiple places | Medium | https://nuxt.com/docs/api/composables/use-state |
| 28 | State | Use Pinia for complex state | Pinia for advanced state management | @pinia/nuxt for complex apps | Custom state management | useMainStore() with Pinia | Custom reactive store implementation | Medium | https://nuxt.com/docs/getting-started/state-management |
| 29 | State | Use callOnce for one-time async operations | Ensure async operations run only once | callOnce for store initialization | Direct await in component | await callOnce(store.fetch) | await store.fetch() on every render | Medium | https://nuxt.com/docs/api/utils/call-once |
| 30 | SEO | Use useSeoMeta for SEO tags | Type-safe SEO meta tag management | useSeoMeta for meta tags | useHead for simple meta | useSeoMeta({ title: 'Home', ogTitle: 'Home', description: '...' }) | useHead({ meta: [{ name: 'description', content: '...' }] }) | High | https://nuxt.com/docs/api/composables/use-seo-meta |
| 31 | SEO | Use reactive values in useSeoMeta | Dynamic SEO tags with refs or getters | Computed getters for dynamic values | Static values for dynamic content | useSeoMeta({ title: () => post.value.title }) | useSeoMeta({ title: post.value.title }) | Medium | https://nuxt.com/docs/api/composables/use-seo-meta |
| 32 | SEO | Use useHead for non-meta head elements | Scripts styles links in head | useHead for scripts and links | useSeoMeta for scripts | useHead({ script: [{ src: '/analytics.js' }] }) | useSeoMeta({ script: '...' }) | Medium | https://nuxt.com/docs/api/composables/use-head |
| 33 | SEO | Include OpenGraph tags | Add OG tags for social sharing | ogTitle ogDescription ogImage | Missing social preview | useSeoMeta({ ogImage: '/og.png', twitterCard: 'summary_large_image' }) | No OG configuration | Medium | https://nuxt.com/docs/api/composables/use-seo-meta |
| 34 | Middleware | Use defineNuxtRouteMiddleware | Define route middleware properly | defineNuxtRouteMiddleware wrapper | export default function | export default defineNuxtRouteMiddleware((to, from) => {}) | export default function(to, from) {} | High | https://nuxt.com/docs/guide/directory-structure/middleware |
| 35 | Middleware | Use navigateTo for redirects | Redirect in middleware with navigateTo | return navigateTo('/login') | router.push in middleware | if (!auth) return navigateTo('/login') | if (!auth) router.push('/login') | High | https://nuxt.com/docs/api/utils/navigate-to |
| 36 | Middleware | Reference middleware in definePageMeta | Apply middleware to specific pages | middleware array in definePageMeta | Global middleware for page-specific | definePageMeta({ middleware: ['auth'] }) | Global auth check for one page | Medium | https://nuxt.com/docs/guide/directory-structure/middleware |
| 37 | Middleware | Use .global suffix for global middleware | Apply middleware to all routes | auth.global.ts for app-wide auth | Manual middleware on every page | middleware/auth.global.ts | middleware: ['auth'] on every page | Medium | https://nuxt.com/docs/guide/directory-structure/middleware |
| 38 | ErrorHandling | Use createError for errors | Create errors with proper status codes | createError with statusCode | throw new Error | throw createError({ statusCode: 404, statusMessage: 'Not Found' }) | throw new Error('Not Found') | High | https://nuxt.com/docs/api/utils/create-error |
| 39 | ErrorHandling | Use NuxtErrorBoundary for local errors | Handle errors within component subtree | NuxtErrorBoundary for component errors | Global error page for local errors | <NuxtErrorBoundary @error="log"><template #error="{ error }"> | error.vue for component errors | Medium | https://nuxt.com/docs/getting-started/error-handling |
| 40 | ErrorHandling | Use clearError to recover from errors | Clear error state and optionally redirect | clearError({ redirect: '/' }) | Manual error state reset | clearError({ redirect: '/home' }) | error.value = null | Medium | https://nuxt.com/docs/api/utils/clear-error |
| 41 | ErrorHandling | Use short statusMessage | Keep statusMessage brief for security | Short generic messages | Detailed error info in statusMessage | createError({ statusCode: 400, statusMessage: 'Bad Request' }) | createError({ statusMessage: 'Invalid user ID: 123' }) | High | https://nuxt.com/docs/getting-started/error-handling |
| 42 | Link | Use NuxtLink for internal navigation | Client-side navigation with prefetching | <NuxtLink to> for internal links | <a href> for internal links | <NuxtLink to="/about">About</NuxtLink> | <a href="/about">About</a> | High | https://nuxt.com/docs/api/components/nuxt-link |
| 43 | Link | Configure prefetch behavior | Control when prefetching occurs | prefetchOn for interaction-based | Default prefetch for low-priority | <NuxtLink prefetch-on="interaction"> | Always default prefetch | Low | https://nuxt.com/docs/api/components/nuxt-link |
| 44 | Link | Use useRouter for programmatic navigation | Navigate programmatically | useRouter().push() for navigation | Direct window.location | const router = useRouter(); router.push('/dashboard') | window.location.href = '/dashboard' | Medium | https://nuxt.com/docs/api/composables/use-router |
| 45 | Link | Use navigateTo in composables | Navigate outside components | navigateTo() in middleware or plugins | useRouter in non-component code | return navigateTo('/login') | router.push in middleware | Medium | https://nuxt.com/docs/api/utils/navigate-to |
| 46 | AutoImports | Leverage auto-imports | Use auto-imported composables directly | Direct use of ref computed useFetch | Manual imports for Nuxt composables | const count = ref(0) | import { ref } from 'vue'; const count = ref(0) | Medium | https://nuxt.com/docs/guide/concepts/auto-imports |
| 47 | AutoImports | Use #imports for explicit imports | Explicit imports when needed | #imports for clarity or disabled auto-imports | import from 'vue' when auto-import enabled | import { ref } from '#imports' | import { ref } from 'vue' | Low | https://nuxt.com/docs/guide/concepts/auto-imports |
| 48 | AutoImports | Configure third-party auto-imports | Add external package auto-imports | imports.presets in nuxt.config | Manual imports everywhere | imports: { presets: [{ from: 'vue-i18n', imports: ['useI18n'] }] } | import { useI18n } everywhere | Low | https://nuxt.com/docs/guide/concepts/auto-imports |
| 49 | Plugins | Use defineNuxtPlugin | Define plugins properly | defineNuxtPlugin wrapper | export default function | export default defineNuxtPlugin((nuxtApp) => {}) | export default function(ctx) {} | High | https://nuxt.com/docs/guide/directory-structure/plugins |
| 50 | Plugins | Use provide for injection | Provide helpers across app | return { provide: {} } for type safety | nuxtApp.provide without types | return { provide: { hello: (name) => `Hello ${name}!` } } | nuxtApp.provide('hello', fn) | Medium | https://nuxt.com/docs/guide/directory-structure/plugins |
| 51 | Plugins | Use .client or .server suffix | Control plugin execution environment | plugin.client.ts for client-only | if (process.client) checks | analytics.client.ts | if (process.client) { // analytics } | Medium | https://nuxt.com/docs/guide/directory-structure/plugins |
| 52 | Environment | Use runtimeConfig for env vars | Access environment variables safely | runtimeConfig in nuxt.config | process.env directly | runtimeConfig: { apiSecret: '', public: { apiBase: '' } } | process.env.API_SECRET in components | High | https://nuxt.com/docs/guide/going-further/runtime-config |
| 53 | Environment | Use NUXT_ prefix for env override | Override config with environment variables | NUXT_API_SECRET NUXT_PUBLIC_API_BASE | Custom env var names | NUXT_PUBLIC_API_BASE=https://api.example.com | API_BASE=https://api.example.com | High | https://nuxt.com/docs/guide/going-further/runtime-config |
| 54 | Environment | Access public config with useRuntimeConfig | Get public config in components | useRuntimeConfig().public | Direct process.env access | const config = useRuntimeConfig(); config.public.apiBase | process.env.NUXT_PUBLIC_API_BASE | High | https://nuxt.com/docs/api/composables/use-runtime-config |
| 55 | Environment | Keep secrets in private config | Server-only secrets in runtimeConfig root | runtimeConfig.apiSecret (server only) | Secrets in public config | runtimeConfig: { dbPassword: '' } | runtimeConfig: { public: { dbPassword: '' } } | High | https://nuxt.com/docs/guide/going-further/runtime-config |
| 56 | Performance | Use Lazy prefix for code splitting | Lazy load components with Lazy prefix | <LazyComponent> for below-fold | Eager load all components | <LazyMountainsList v-if="show"/> | <MountainsList/> for hidden content | Medium | https://nuxt.com/docs/guide/directory-structure/components |
| 57 | Performance | Use useLazyFetch for non-blocking data | Alias for useFetch with lazy: true | useLazyFetch for secondary data | useFetch for all requests | const { data } = useLazyFetch('/api/comments') | await useFetch for comments section | Medium | https://nuxt.com/docs/api/composables/use-lazy-fetch |
| 58 | Performance | Use lazy hydration for interactivity | Delay component hydration until needed | LazyComponent with hydration strategy | Immediate hydration for all | <LazyModal hydrate-on-visible/> | <Modal/> in footer | Low | https://nuxt.com/docs/guide/going-further/experimental-features |