Claude-skills nuxt-core
install
source · Clone the upstream repo
git clone https://github.com/secondsky/claude-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/secondsky/claude-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/nuxt-v4/skills/nuxt-core" ~/.claude/skills/secondsky-claude-skills-nuxt-core && rm -rf "$T"
manifest:
plugins/nuxt-v4/skills/nuxt-core/SKILL.mdsource content
Nuxt 4 Core Fundamentals
Project setup, configuration, routing, SEO, and error handling for Nuxt 4 applications.
Quick Reference
Version Requirements
| Package | Minimum | Recommended |
|---|---|---|
| nuxt | 4.0.0 | 4.2.x |
| vue | 3.5.0 | 3.5.x |
| nitro | 2.10.0 | 2.12.x |
| vite | 6.0.0 | 6.2.x |
| typescript | 5.0.0 | 5.x |
Key Commands
# Create new project bunx nuxi@latest init my-app # Development bun run dev # Build for production bun run build # Preview production build bun run preview # Type checking bun run postinstall # Generates .nuxt directory bunx nuxi typecheck # Add a page/component/composable bunx nuxi add page about bunx nuxi add component MyButton bunx nuxi add composable useAuth
Directory Structure (Nuxt v4)
my-nuxt-app/ ├── app/ # Default srcDir in v4 │ ├── assets/ # Build-processed assets (CSS, images) │ ├── components/ # Auto-imported Vue components │ ├── composables/ # Auto-imported composables │ ├── layouts/ # Layout components │ ├── middleware/ # Route middleware │ ├── pages/ # File-based routing │ ├── plugins/ # Nuxt plugins │ ├── utils/ # Auto-imported utility functions │ ├── app.vue # Main app component │ ├── app.config.ts # App-level runtime config │ ├── error.vue # Error page component │ └── router.options.ts # Router configuration │ ├── server/ # Server-side code (Nitro) │ ├── api/ # API endpoints │ ├── middleware/ # Server middleware │ ├── plugins/ # Nitro plugins │ ├── routes/ # Server routes │ └── utils/ # Server utilities │ ├── public/ # Static assets (served from root) ├── shared/ # Shared code (app + server) ├── content/ # Nuxt Content files (if using) ├── layers/ # Nuxt layers ├── modules/ # Local modules ├── .nuxt/ # Generated files (git ignored) ├── .output/ # Build output (git ignored) ├── nuxt.config.ts # Nuxt configuration ├── tsconfig.json # TypeScript configuration └── package.json # Dependencies
Key Change in v4: The
app/ directory is now the default srcDir. All app code goes in app/, server code stays in server/.
When to Load References
Load
when:references/configuration-deep.md
- Configuring advanced nuxt.config.ts options
- Setting up modules and plugins
- Customizing Vite or Nitro configuration
- Configuring experimental features
Load
when:references/routing-advanced.md
- Implementing complex routing patterns
- Creating route middleware with authentication
- Using catch-all routes or optional parameters
- Configuring router options
Load
when:references/plugins-architecture.md
- Creating Nuxt plugins
- Injecting global utilities or composables
- Integrating third-party libraries
- Understanding plugin execution order
Configuration
Basic nuxt.config.ts
export default defineNuxtConfig({ // Enable Nuxt 4 features future: { compatibilityVersion: 4 }, // Development tools devtools: { enabled: true }, // Modules modules: [ '@nuxt/ui', '@nuxt/content', '@nuxt/image' ], // Runtime config (environment variables) runtimeConfig: { // Server-only (not exposed to client) apiSecret: process.env.API_SECRET, databaseUrl: process.env.DATABASE_URL, // Public (client + server) public: { apiBase: process.env.API_BASE || 'https://api.example.com', appName: 'My App' } }, // App config app: { head: { title: 'My Nuxt App', meta: [ { charset: 'utf-8' }, { name: 'viewport', content: 'width=device-width, initial-scale=1' } ] } }, // Nitro config (server) nitro: { preset: 'cloudflare-pages' }, // TypeScript typescript: { strict: true, typeCheck: true } })
Runtime Config Usage
// In composables or components const config = useRuntimeConfig() // Public values (client + server) const apiBase = config.public.apiBase // Server-only values (only in server/) const apiSecret = config.apiSecret // undefined on client!
Critical Rule: Always use
useRuntimeConfig() instead of process.env for environment variables in production.
App Config vs Runtime Config
| Feature | App Config | Runtime Config |
|---|---|---|
| Location | | |
| Hot reload | Yes | No |
| Secrets | No | Yes (server-only) |
| Use case | UI settings, themes | API keys, URLs |
// app/app.config.ts - UI settings (hot-reloadable) export default defineAppConfig({ theme: { primaryColor: '#3490dc' }, ui: { rounded: 'lg' } }) // Usage const appConfig = useAppConfig() const color = appConfig.theme.primaryColor
Routing
File-Based Routing
app/pages/ ├── index.vue → / ├── about.vue → /about ├── users/ │ ├── index.vue → /users │ └── [id].vue → /users/:id └── blog/ ├── index.vue → /blog ├── [slug].vue → /blog/:slug └── [...slug].vue → /blog/* (catch-all)
Dynamic Routes
<!-- app/pages/users/[id].vue --> <script setup lang="ts"> const route = useRoute() // Get route params const userId = route.params.id // Reactive (updates when route changes) const userId = computed(() => route.params.id) // Fetch user data const { data: user } = await useFetch(`/api/users/${userId.value}`) </script> <template> <div> <h1>{{ user?.name }}</h1> </div> </template>
Navigation
<script setup> const goToUser = (id: string) => { navigateTo(`/users/${id}`) } const goBack = () => { navigateTo(-1) // Go back in history } // With options const goToLogin = () => { navigateTo('/login', { replace: true, // Replace current history entry external: false // Internal navigation }) } </script> <template> <!-- Declarative navigation --> <NuxtLink to="/about">About</NuxtLink> <NuxtLink :to="`/users/${user.id}`">View User</NuxtLink> <!-- Prefetching (default: on hover) --> <NuxtLink to="/dashboard" prefetch>Dashboard</NuxtLink> <!-- No prefetch --> <NuxtLink to="/admin" :prefetch="false">Admin</NuxtLink> </template>
Route Middleware
// app/middleware/auth.ts export default defineNuxtRouteMiddleware((to, from) => { const { isAuthenticated } = useAuth() if (!isAuthenticated.value) { return navigateTo('/login') } })
<!-- app/pages/dashboard.vue --> <script setup lang="ts"> definePageMeta({ middleware: 'auth' }) </script>
Global Middleware
// app/middleware/analytics.global.ts export default defineNuxtRouteMiddleware((to, from) => { // Runs on every route change if (import.meta.client) { window.gtag?.('event', 'page_view', { page_path: to.path }) } })
Page Meta
<script setup lang="ts"> definePageMeta({ title: 'Dashboard', middleware: ['auth'], layout: 'admin', pageTransition: { name: 'fade' }, keepalive: true }) </script>
SEO & Meta Tags
useSeoMeta (Recommended)
<script setup lang="ts"> useSeoMeta({ title: 'My Page Title', description: 'Page description for search engines', ogTitle: 'My Page Title', ogDescription: 'Page description', ogImage: 'https://example.com/og-image.jpg', ogUrl: 'https://example.com/my-page', twitterCard: 'summary_large_image', twitterTitle: 'My Page Title', twitterDescription: 'Page description', twitterImage: 'https://example.com/og-image.jpg' }) </script>
useHead (More Control)
<script setup lang="ts"> useHead({ title: 'My Page Title', meta: [ { name: 'description', content: 'Page description' }, { property: 'og:title', content: 'My Page Title' } ], link: [ { rel: 'canonical', href: 'https://example.com/my-page' } ], script: [ { src: 'https://example.com/script.js', defer: true } ] }) </script>
Dynamic Meta Tags
<script setup lang="ts"> const { data: post } = await useFetch(`/api/posts/${route.params.slug}`) useSeoMeta({ title: () => post.value?.title, description: () => post.value?.excerpt, ogImage: () => post.value?.image }) </script>
Title Template
// nuxt.config.ts export default defineNuxtConfig({ app: { head: { titleTemplate: '%s | My App' // "Page Title | My App" } } })
Error Handling
Error Page
<!-- app/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>
Error Boundaries (Component-Level)
<template> <NuxtErrorBoundary @error="handleError"> <template #error="{ error, clearError }"> <div class="error-container"> <h2>Something went wrong</h2> <p>{{ error.message }}</p> <button @click="clearError">Try again</button> </div> </template> <!-- Your component content --> <MyComponent /> </NuxtErrorBoundary> </template> <script setup> const handleError = (error: Error) => { console.error('Component error:', error) // Send to error tracking service } </script>
Throwing Errors
// In pages or components throw createError({ statusCode: 404, statusMessage: 'Page Not Found', fatal: true // Shows error page, stops rendering }) // Non-fatal error (shows inline) throw createError({ statusCode: 400, message: 'Invalid input' })
API Error Handling
const { data, error } = await useFetch('/api/users') if (error.value) { // Handle error gracefully showError({ statusCode: error.value.statusCode, message: error.value.message }) }
Common Anti-Patterns
Using process.env Instead of Runtime Config
// WRONG - Won't work in production! const apiUrl = process.env.API_URL // CORRECT const config = useRuntimeConfig() const apiUrl = config.public.apiBase
Missing Middleware Guards
// WRONG - No return, middleware continues export default defineNuxtRouteMiddleware((to) => { const { isAuthenticated } = useAuth() if (!isAuthenticated.value) { navigateTo('/login') // Missing return! } }) // CORRECT export default defineNuxtRouteMiddleware((to) => { const { isAuthenticated } = useAuth() if (!isAuthenticated.value) { return navigateTo('/login') // Return stops middleware chain } })
Non-Reactive Route Params
// WRONG - Not reactive const userId = route.params.id // CORRECT - Reactive const userId = computed(() => route.params.id)
Troubleshooting
Build Errors / Type Errors:
rm -rf .nuxt .output node_modules/.vite && bun install && bun run dev
Route Not Found:
- Check file is in
(not rootapp/pages/
)pages/ - Verify file extension is
.vue - Check for typos in dynamic params
[id].vue
Middleware Not Running:
- Ensure file has
suffix for global middleware.global.ts - Check
matches filenamedefinePageMeta({ middleware: 'name' }) - Verify middleware returns
or nothingnavigateTo()
Meta Tags Not Updating:
- Use reactive values:
title: () => post.value?.title - Ensure
is called inuseSeoMeta<script setup>
Related Skills
- nuxt-data: Composables, data fetching, state management
- nuxt-server: Server routes, API patterns, database integration
- nuxt-production: Performance, testing, deployment
- nuxt-ui-v4: Nuxt UI component library
Templates Available
See
templates/ directory for:
- Production-ready
nuxt.config.ts
with proper structureapp.vue- Middleware examples
Version: 4.0.0 | Last Updated: 2025-12-28 | License: MIT