Generated banner for Nuxt
Generated logo for Nuxt

Nuxt

Anthony FuCommunityCreated by Anthony Fu

  • Skill
  • Tool integration
  • Software development
  • MIT
  • Low risk

From the author

Nuxt full-stack Vue framework with SSR, auto-imports, and file-based routing. Use when working with Nuxt apps, server routes, useFetch, middleware, or hybrid rendering.
— the description by Anthony Fu

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

  1. Download the skill as a ZIP:

    https://mp-staging.autogineer.com/agent-skills/antfu/nuxt/download
  2. Unzip 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 srcDir is app/ — Vue app code (app.vue, components/, composables/, pages/, etc.) lives under app/, while server/, shared/, public/, modules/, layers/ and nuxt.config.ts stay at the project root. The ~/@ aliases point at app/; 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 from nuxt/server and are no longer auto-imported by default, $fetch/useFetch are typed from generated route types, routing is case-sensitive and typed by default, and the bundler is Vite 8 (Rolldown). Nuxt 5 requires Node 22.19/24.11+. A Nitro v2 compatibility layer (nitroLegacy) keeps older server code building during migration. See individual references for details.

Core

TopicDescriptionReference
Directory StructureNuxt 4 app/ srcDir, shared/, aliases, conventionscore-directory-structure
Configurationnuxt.config.ts, app.config.ts, aliases, Nuxt 5 defaults, jiti removal, experimentalcore-config
CLI CommandsDev server, build, generate, preview, and utility commandscore-cli
RoutingFile-based routing, case-sensitive + typed routes, named views, layout props, middlewarecore-routing
Data FetchinguseFetch, useAsyncData, $fetch, typed routes, createUseFetch factories, cachingcore-data-fetching
ModulesCreating and using Nuxt modules, Nuxt Kit utilitiescore-modules
DeploymentPlatform-agnostic deployment with Nitro, Vercel, Netlify, Cloudflarecore-deployment

Features

TopicDescriptionReference
Composables Auto-importsVue/Nuxt composables, custom composables, shared/, useAnnouncerfeatures-composables
Components Auto-importsComponent naming, lazy loading, hydration strategiesfeatures-components-autoimport
Built-in ComponentsNuxtLink, NuxtPage, NuxtLayout, NuxtAnnouncer, ClientOnly, and morefeatures-components
State ManagementuseState composable, SSR-friendly state, Pinia integrationfeatures-state
Server RoutesAPI routes, nuxt/server imports, Nitro v3 (h3 v2), sessions, middlewarefeatures-server

Rendering

TopicDescriptionReference
Rendering ModesUniversal (SSR), client-side (SPA), hybrid rendering, route rulesrendering-modes

Best Practices

TopicDescriptionReference
Data Fetching PatternsEfficient fetching, caching, parallel requests, error handlingbest-practices-data-fetching
SSR & HydrationAvoiding context leaks, hydration mismatches, composable patternsbest-practices-ssr

Advanced

TopicDescriptionReference
LayersExtending applications with reusable layersadvanced-layers
Lifecycle HooksBuild-time, runtime, and server hooksadvanced-hooks
Module AuthoringPublishable modules with Nuxt Kit, keyed composables, dependenciesadvanced-module-authoring

Files

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

ScenarioUse
Component initial datauseFetch or useAsyncData
User interactions (clicks, forms)$fetch
Third-party SDK/APIuseAsyncData with custom function
Multiple parallel requestsuseAsyncData with Promise.all
Reusable API client with shared defaultscreateUseFetch 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
PatternUse 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.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download nuxt.zip