nuxt-development
SKILL.md
Nuxt.js Development Standards
Apply these standards when developing Nuxt.js 3 applications. For general Vue coding standards, also refer to the vue-development skill.
Prerequisites
This skill extends Vue.js development standards. Follow all Vue 3 Composition API guidelines unless specifically overridden here.
Nuxt-Specific Guidelines
Pages (File-Based Routing)
- Place all route pages in the
pages/directory - File and folder names automatically define the route structure
- Use
<script setup>at the top, followed by<template>, then<style scoped> - Use TypeScript for all pages when possible
Example structure:
pages/
├── index.vue # Route: /
├── about.vue # Route: /about
├── users/
│ ├── index.vue # Route: /users
│ └── [id].vue # Route: /users/:id
└── [...slug].vue # Catch-all route
Layouts
- Place shared layouts in the
layouts/directory - Use
default.vuefor the main app shell - Use
<NuxtLayout />component to switch layouts dynamically - Use
definePageMetacomposable for page-specific metadata
Example layout:
<script setup lang="ts">
// layouts/default.vue
</script>
<template>
<div class="layout">
<header>
<nav><!-- Navigation --></nav>
</header>
<main>
<slot />
</main>
<footer><!-- Footer --></footer>
</div>
</template>
Using layouts:
<script setup lang="ts">
definePageMeta({
layout: 'custom'
})
</script>
Auto-Import Components
- Place reusable components in the
components/directory - Nuxt auto-imports components - no manual imports needed
- Use PascalCase for component file names
- Organize with subdirectories for namespacing
Example:
components/
├── AppHeader.vue # <AppHeader />
├── TheFooter.vue # <TheFooter />
└── user/
└── UserCard.vue # <UserCard /> or <UserUserCard />
Composables
- Place composable functions in the
composables/directory - Nuxt auto-imports composables starting with
use - Use Nuxt-specific composables:
useRoute()- Current route informationuseRouter()- Router instanceuseAsyncData()- Fetch data with SSR supportuseFetch()- Simplified data fetchinguseState()- Shared state across componentsuseCookie()- Cookie managementuseHead()- Meta tags and SEO
Example composable:
// composables/useAuth.ts
export const useAuth = () => {
const user = useState('user', () => null)
const isAuthenticated = computed(() => !!user.value)
const login = async credentials => {
const data = await $fetch('/api/auth/login', {
method: 'POST',
body: credentials
})
user.value = data.user
}
const logout = () => {
user.value = null
}
return {
user,
isAuthenticated,
login,
logout
}
}
Data Fetching
Use useFetch or useAsyncData for SSR-compatible data fetching:
<script setup lang="ts">
// Automatic key generation
const { data: users } = await useFetch('/api/users')
// Manual key for more control
const { data: user, refresh } = await useAsyncData('user', () => $fetch(`/api/users/${route.params.id}`))
// With options
const { data, pending, error } = await useFetch('/api/data', {
method: 'POST',
body: { query: 'search' },
lazy: true, // Don't block navigation
server: true, // SSR
watch: [searchQuery] // Re-fetch on change
})
</script>
Server Routes (API)
- Place API routes in the
server/api/directory - Use event handlers with
defineEventHandler
Example:
// server/api/users/[id].get.ts
export default defineEventHandler(async event => {
const id = getRouterParam(event, 'id')
// Fetch user from database
const user = await prisma.user.findUnique({
where: { id: parseInt(id) }
})
if (!user) {
throw createError({
statusCode: 404,
message: 'User not found'
})
}
return user
})
Middleware
- Place middleware in the
middleware/directory - Auto-imported and available globally or per-page
Example:
// middleware/auth.ts
export default defineNuxtRouteMiddleware((to, from) => {
const auth = useAuth()
if (!auth.isAuthenticated.value) {
return navigateTo('/login')
}
})
Apply to page:
<script setup lang="ts">
definePageMeta({
middleware: ['auth']
})
</script>
Directory Structure
Follow Nuxt's conventions:
project/
├── .nuxt/ # Build output (auto-generated)
├── assets/ # Uncompiled assets (CSS, images)
├── components/ # Auto-imported components
├── composables/ # Auto-imported composables
├── layouts/ # App layouts
├── middleware/ # Route middleware
├── pages/ # File-based routing
├── plugins/ # App plugins
├── public/ # Static assets
├── server/ # Server-side code
│ ├── api/ # API endpoints
│ ├── middleware/ # Server middleware
│ └── routes/ # Server routes
├── stores/ # Pinia stores (if using state management)
├── nuxt.config.ts # Nuxt configuration
└── app.vue # Root component (optional)
Configuration
nuxt.config.ts:
export default defineNuxtConfig({
devtools: { enabled: true },
modules: ['@nuxtjs/tailwindcss', '@pinia/nuxt'],
runtimeConfig: {
// Private (server-only)
apiSecret: process.env.API_SECRET,
// Public (client + server)
public: {
apiBase: process.env.API_BASE_URL || '/api'
}
},
app: {
head: {
title: 'My Nuxt App',
meta: [{ charset: 'utf-8' }, { name: 'viewport', content: 'width=device-width, initial-scale=1' }]
}
}
})
Example Page Component
<script setup lang="ts">
import { ref } from 'vue'
// Auto-imported composables
const route = useRoute()
const router = useRouter()
// Page metadata
definePageMeta({
layout: 'default',
middleware: ['auth']
})
// SEO
useHead({
title: 'User Profile',
meta: [{ name: 'description', content: 'User profile page' }]
})
// Data fetching with SSR
const { data: user, pending } = await useFetch(`/api/users/${route.params.id}`)
// Local state
const isEditing = ref(false)
const saveProfile = async () => {
await $fetch(`/api/users/${route.params.id}`, {
method: 'PUT',
body: user.value
})
isEditing.value = false
}
</script>
<template>
<div class="profile">
<h1 v-if="pending">Loading...</h1>
<div v-else-if="user">
<h1>{{ user.name }}</h1>
<p>{{ user.email }}</p>
<button @click="isEditing = !isEditing">
{{ isEditing ? 'Cancel' : 'Edit' }}
</button>
<button v-if="isEditing" @click="saveProfile">Save</button>
</div>
</div>
</template>
<style scoped>
.profile {
padding: 2rem;
}
</style>
Best Practices
- Use auto-imports - No need to manually import Vue, Nuxt composables, or components
- SSR-first - Always consider server-side rendering in data fetching
- Use
useFetch- Prefer over manualfetchfor automatic SSR handling - Type safety - Enable TypeScript for better DX
- Middleware - Use for authentication, redirects, and data validation
- State management - Use
useStatefor simple shared state, Pinia for complex state - Error handling - Use
throw createError()for consistent error pages
When to Apply
Apply these standards when:
- Creating Nuxt.js projects
- Building pages and layouts
- Writing composables
- Creating API endpoints
- Implementing SSR/SSG
- User asks about Nuxt.js patterns
Weekly Installs
2
Repository
devbyray/github…-starterGitHub Stars
4
First Seen
14 days ago
Security Audits
Installed on
gemini-cli2
opencode2
codebuddy2
github-copilot2
codex2
kimi-cli2