AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
Nuxt is a full-stack Vue framework designed for universal deployment. This skill provides essential guidance on Nuxt 5, covering its directory structure, file-based routing, server-side rendering, and Nitro-based server engine. It includes best practices for data fetching, state management, and modular architecture, helping developers leverage features like auto-imports and hybrid rendering.
What it does
- Explains the Nuxt 5 directory structure and configuration conventions.
- Details file-based routing, including typed and case-sensitive routes.
- Provides guidance on data fetching using useFetch and $fetch.
- Covers server-side rendering, hybrid rendering, and Nitro server integration.
- Explains how to extend applications using Nuxt Layers and modules.
- Outlines lifecycle hooks for build-time and runtime customization.
When to use it
- When starting a new full-stack project with Vue and Nuxt.
- When migrating existing projects to Nuxt 5 and Nitro v3.
- When implementing server-side rendering or hybrid rendering strategies.
- When creating reusable application layers or custom Nuxt modules.
- When optimizing data fetching patterns and state management in SSR environments.
Advantages
- Provides standardized patterns for Nuxt 5 development.
- Simplifies complex concepts like Nitro server integration and lifecycle hooks.
- Offers clear guidance on project structure and file-based routing.
- Supports efficient code reuse through the Nuxt Layers system.
Usage examples
- “How do I structure my Nuxt 5 project directory?”
- The agent explains the default app/ directory structure for Vue code and the root-level placement for server/, public/, and nuxt.config.ts files.
- “Explain how to use layers to share components between projects.”
- The agent provides instructions on using the extends property in nuxt.config.ts to import layers from local directories, git repositories, or npm packages.
- “How do I hook into the page navigation lifecycle?”
- The agent shows how to use nuxtApp.hook('page:finish', ...) within a plugin to execute code after navigation completes.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/antfu/nuxt/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip nuxt.zip -d .agents/skills
Documentation by Anthony Fu
Nuxt is a full-stack Vue framework that provides server-side rendering, file-based routing, auto-imports, and a powerful module system. It uses Nitro as its server engine for universal deployment across Node.js, serverless, and edge platforms.
The skill is based on Nuxt 5.x (
5.0.0-0), generated at 2026-11-05.
Directory layout: the default
srcDirisapp/— Vue app code (app.vue,components/,composables/,pages/, etc.) lives underapp/, whileserver/,shared/,public/,modules/,layers/andnuxt.config.tsstay at the project root. The~/@aliases point atapp/; use~~/@@for the root.
Nuxt 5 note: Nuxt 5 makes many former experimental flags the default. The biggest shifts for agents: server code moves to Nitro v3 (h3 v2, Web-standard
Request/Response), server utilities import fromnuxt/serverand are no longer auto-imported by default,$fetch/useFetchare typed from generated route types, routing is case-sensitive and typed by default, and the bundler is Vite 8 (Rolldown). Nuxt 5 requires Node22.19/24.11+. A Nitro v2 compatibility layer (nitroLegacy) keeps older server code building during migration. See individual references for details.
Core
| Topic | Description | Reference |
|---|---|---|
| Directory Structure | Nuxt 4 app/ srcDir, shared/, aliases, conventions | core-directory-structure |
| Configuration | nuxt.config.ts, app.config.ts, aliases, Nuxt 5 defaults, jiti removal, experimental | core-config |
| CLI Commands | Dev server, build, generate, preview, and utility commands | core-cli |
| Routing | File-based routing, case-sensitive + typed routes, named views, layout props, middleware | core-routing |
| Data Fetching | useFetch, useAsyncData, $fetch, typed routes, createUseFetch factories, caching | core-data-fetching |
| Modules | Creating and using Nuxt modules, Nuxt Kit utilities | core-modules |
| Deployment | Platform-agnostic deployment with Nitro, Vercel, Netlify, Cloudflare | core-deployment |
Features
| Topic | Description | Reference |
|---|---|---|
| Composables Auto-imports | Vue/Nuxt composables, custom composables, shared/, useAnnouncer | features-composables |
| Components Auto-imports | Component naming, lazy loading, hydration strategies | features-components-autoimport |
| Built-in Components | NuxtLink, NuxtPage, NuxtLayout, NuxtAnnouncer, ClientOnly, and more | features-components |
| State Management | useState composable, SSR-friendly state, Pinia integration | features-state |
| Server Routes | API routes, nuxt/server imports, Nitro v3 (h3 v2), sessions, middleware | features-server |
Rendering
| Topic | Description | Reference |
|---|---|---|
| Rendering Modes | Universal (SSR), client-side (SPA), hybrid rendering, route rules | rendering-modes |
Best Practices
| Topic | Description | Reference |
|---|---|---|
| Data Fetching Patterns | Efficient fetching, caching, parallel requests, error handling | best-practices-data-fetching |
| SSR & Hydration | Avoiding context leaks, hydration mismatches, composable patterns | best-practices-ssr |
Advanced
| Topic | Description | Reference |
|---|---|---|
| Layers | Extending applications with reusable layers | advanced-layers |
| Lifecycle Hooks | Build-time, runtime, and server hooks | advanced-hooks |
| Module Authoring | Publishable modules with Nuxt Kit, keyed composables, dependencies | advanced-module-authoring |
Files
- ATTRIBUTION.mdAdded by AutoGineer889 B
- GENERATION.md134 B
- LICENSELicence file added by AutoGineer1.1 KiB
- SKILL.md4.5 KiB
- references/advanced-hooks.md7.2 KiB
- references/advanced-layers.md5.1 KiB
- references/advanced-module-authoring.md16.5 KiB
- references/best-practices-data-fetching.md9.2 KiB
- references/best-practices-ssr.md7.5 KiB
- references/core-cli.md4.8 KiB
- references/core-config.md10.5 KiB
- references/core-data-fetching.md11.4 KiB
- references/core-deployment.md5.1 KiB
- references/core-directory-structure.md9.8 KiB
- references/core-modules.md5.4 KiB
- references/core-routing.md6.8 KiB
- references/features-components-autoimport.md5.9 KiB
- references/features-components.md6.1 KiB
- references/features-composables.md7.2 KiB
- references/features-server.md11.1 KiB
- references/features-state.md3.9 KiB
- references/rendering-modes.md5.5 KiB
references/best-practices-data-fetching.md
name: data-fetching-best-practices description: Patterns and best practices for efficient data fetching in Nuxt
Data Fetching Best Practices
Effective data fetching patterns for SSR-friendly, performant Nuxt applications.
Choose the Right Tool
| Scenario | Use |
|---|---|
| Component initial data | useFetch or useAsyncData |
| User interactions (clicks, forms) | $fetch |
| Third-party SDK/API | useAsyncData with custom function |
| Multiple parallel requests | useAsyncData with Promise.all |
| Reusable API client with shared defaults | createUseFetch factory |
Await vs Non-Await Usage
The await keyword controls whether data fetching blocks navigation:
With await - Blocking Navigation
<script setup lang="ts">
// Navigation waits until data is fetched (uses Vue Suspense)
const { data } = await useFetch('/api/posts')
// data.value is available immediately after this line
</script>Server: Fetches data and includes it in the payload
Client hydration: Uses payload data, no re-fetch
Client navigation: Blocks until data is ready
Without await - Non-Blocking (Lazy)
<script setup lang="ts">
// Navigation proceeds immediately, data fetches in background
const { data, status } = useFetch('/api/posts', { lazy: true })
// data.value may be undefined initially - check status!
</script>
<template>
<div v-if="status === 'pending'">Loading...</div>
<div v-else>{{ data }}</div>
</template>Equivalent to using useLazyFetch:
<script setup lang="ts">
const { data, status } = useLazyFetch('/api/posts')
</script>When to Use Each
| Pattern | Use Case |
|---|---|
await useFetch() | Critical data needed for SEO/initial render |
useFetch({ lazy: true }) | Non-critical data, better perceived performance |
await useLazyFetch() | Same as lazy, await only ensures initialization |
Avoid Double Fetching
❌ Wrong: Using $fetch Alone in Setup
<script setup lang="ts">
// This fetches TWICE: once on server, once on client
const data = await $fetch('/api/posts')
</script>✅ Correct: Use useFetch
<script setup lang="ts">
// Fetches on server, hydrates on client (no double fetch)
const { data } = await useFetch('/api/posts')
</script>Use Explicit Cache Keys
❌ Avoid: Auto-generated Keys
<script setup lang="ts">
// Key is auto-generated from file/line - can cause issues
const { data } = await useAsyncData(() => fetchPosts())
</script>✅ Better: Explicit Keys
<script setup lang="ts">
// Explicit key for predictable caching
const { data } = await useAsyncData(
'posts',
() => fetchPosts(),
)
// Dynamic keys for parameterized data
const route = useRoute()
const { data: post } = await useAsyncData(
`post-${route.params.id}`,
() => fetchPost(route.params.id),
)
</script>Handle Loading States Properly
<script setup lang="ts">
const { data, status, error } = await useFetch('/api/posts')
</script>
<template>
<div v-if="status === 'pending'">
<SkeletonLoader />
</div>
<div v-else-if="error">
<ErrorMessage :error="error" />
</div>
<div v-else>
<PostList :posts="data" />
</div>
</template>Use Lazy Fetching for Non-critical Data
<script setup lang="ts">
const id = useRoute().params.id
// Critical data - blocks navigation
const { data: post } = await useFetch(`/api/posts/${id}`)
// Non-critical data - doesn't block navigation
const { data: comments, status } = useFetch(`/api/posts/${id}/comments`, {
lazy: true,
})
// Or use useLazyFetch
const { data: related } = useLazyFetch(`/api/posts/${id}/related`)
</script>
<template>
<article>
<h1>{{ post?.title }}</h1>
<p>{{ post?.content }}</p>
</article>
<section v-if="status === 'pending'">Loading comments...</section>
<CommentList v-else :comments="comments" />
</template>Minimize Payload Size
Use pick for Simple Filtering
<script setup lang="ts">
const { data } = await useFetch('/api/users', {
// Only include these fields in payload
pick: ['id', 'name', 'avatar'],
})
</script>Use transform for Complex Transformations
<script setup lang="ts">
const { data } = await useFetch('/api/posts', {
transform: (posts) => {
return posts.map(post => ({
id: post.id,
title: post.title,
excerpt: post.content.slice(0, 100),
date: new Date(post.createdAt).toLocaleDateString(),
}))
},
})
</script>Parallel Fetching
Fetch Independent Data with useAsyncData
<script setup lang="ts">
const { data } = await useAsyncData(
'dashboard',
async (_nuxtApp, { signal }) => {
const [user, posts, stats] = await Promise.all([
$fetch('/api/user', { signal }),
$fetch('/api/posts', { signal }),
$fetch('/api/stats', { signal }),
])
return { user, posts, stats }
},
)
</script>Multiple useFetch Calls
<script setup lang="ts">
// These run in parallel automatically
const [{ data: user }, { data: posts }] = await Promise.all([
useFetch('/api/user'),
useFetch('/api/posts'),
])
</script>Efficient Refresh Patterns
Watch Reactive Dependencies
<script setup lang="ts">
const page = ref(1)
const category = ref('all')
const { data } = await useFetch('/api/posts', {
query: { page, category },
// Auto-refresh when these change
watch: [page, category],
})
</script>Manual Refresh
<script setup lang="ts">
const { data, refresh, status } = await useFetch('/api/posts')
async function refreshPosts() {
await refresh()
}
</script>Conditional Fetching
<script setup lang="ts">
const userId = ref<string | null>(null)
const { data, execute } = useFetch(() => `/api/users/${userId.value}`, {
immediate: false, // Don't fetch until userId is set
})
// Later, when userId is available
function loadUser(id: string) {
userId.value = id
execute()
}
</script>Server-only Fetching
<script setup lang="ts">
// Only fetch on server, skip on client navigation
const { data } = await useFetch('/api/static-content', {
server: true,
lazy: true,
getCachedData: (key, nuxtApp) => nuxtApp.payload.data[key],
})
</script>Error Handling
<script setup lang="ts">
const { data, error, refresh } = await useFetch('/api/posts')
// Watch for errors if need event-like handling
watch(error, (err) => {
if (err) {
console.error('Fetch failed:', err)
// Show toast, redirect, etc.
}
}, { immediate: true })
</script>
<template>
<div v-if="error">
<p>Failed to load: {{ error.message }}</p>
<button @click="refresh()">Retry</button>
</div>
</template>Shared Data Across Components
<!-- ComponentA.vue -->
<script setup lang="ts">
const { data } = await useFetch('/api/user', { key: 'current-user' })
</script>
<!-- ComponentB.vue -->
<script setup lang="ts">
// Access cached data without refetching
const { data: user } = useNuxtData('current-user')
// Or refresh it
const { refresh } = await useFetch('/api/user', { key: 'current-user' })
</script>Centralize API Config with createUseFetch
Instead of hand-rolling a wrapper around useFetch (and worrying about whether to await it), use the createUseFetch factory. It produces a fully typed composable with shared baseURL, headers, and interceptors:
// app/composables/useAPI.ts
export const useAPI = createUseFetch({
baseURL: 'https://api.nuxt.com',
onRequest({ options }) {
const { session } = useUserSession()
if (session.value?.token) {
options.headers.set('Authorization', `Bearer ${session.value.token}`)
}
},
async onResponseError({ response }) {
if (response.status === 401) await navigateTo('/login')
},
})<script setup lang="ts">
const { data: profile } = await useAPI('/me') // auth + 401 handling baked in
</script>To add reusable behavior (polling, refresh on focus, extra options or return values), pass addons built with defineUseFetchAddon / defineUseAsyncDataAddon to the factory (v4.6+); see core-data-fetching.
For lower-level control, create a custom $fetch instance in a plugin and wrap it with useAsyncData (or pass it to createUseFetch) — this avoids double fetching during SSR:
// app/plugins/api.ts
export default defineNuxtPlugin(() => {
const api = $fetch.create({ baseURL: 'https://api.nuxt.com' })
return { provide: { api } }
})<script setup lang="ts">
const { $api } = useNuxtApp()
const { data } = await useAsyncData('modules', () => $api('/modules'))
</script>Avoid useAsyncData for Side Effects
❌ Wrong: Side Effects in useAsyncData
<script setup lang="ts">
// Don't trigger Pinia actions or side effects
await useAsyncData(() => store.fetchUser()) // Can cause issues
</script>✅ Correct: Use callOnce for Side Effects
<script setup lang="ts">
await callOnce(async () => {
await store.fetchUser()
})
</script><!-- Source references: - https://nuxt.com/docs/4.x/getting-started/data-fetching - https://nuxt.com/docs/4.x/api/composables/use-fetch - https://nuxt.com/docs/4.x/api/composables/use-async-data - https://nuxt.com/docs/4.x/api/composables/use-lazy-fetch - https://nuxt.com/docs/4.x/guide/recipes/custom-usefetch -->
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Security
What our scanner noticed· Low risk
- Instructions that use the network
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- Anthony Fu
- Publisher
- Anthony Fu (Community)
- Source
- antfu/skills · skills/nuxt · commit f63e9ec
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE.md)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Anthony Fu or Anthony Fu.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download nuxt.zip