All skills
onmax avatar

/nuxt

@de09c7b official
by Maxonmax/nuxt-skills715 stars
38

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.

Use this Skill: https://skilld.dev/gh/onmax/nuxt-skills/nuxt

This session only. Nothing lands on disk.

referencesadvanced-module-authoring.md

≈3.3k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Module Authoring

This guide covers creating publishable Nuxt modules with proper structure, type safety, and best practices.

Module Structure

Recommended structure for a publishable module:

my-nuxt-module/
├── src/
│   ├── module.ts          # Module entry
│   └── runtime/
│       ├── components/    # Vue components
│       ├── composables/   # Composables
│       ├── plugins/       # Nuxt plugins
│       └── server/        # Server handlers
├── playground/            # Development app
├── package.json
└── tsconfig.json

Module Definition

Basic Module with Type-safe Options

// src/module.ts
import { defineNuxtModule, createResolver, addPlugin, addComponent, addImports } from '@nuxt/kit'

export interface ModuleOptions {
  prefix?: string
  apiKey: string
  enabled?: boolean
}

export default defineNuxtModule<ModuleOptions>({
  meta: {
    name: 'my-module',
    configKey: 'myModule',
    compatibility: {
      nuxt: '>=3.0.0',
    },
  },
  defaults: {
    prefix: 'My',
    enabled: true,
  },
  setup(options, nuxt) {
    if (!options.enabled) return

    const { resolve } = createResolver(import.meta.url)

    // Module setup logic here
  },
})

Using .with() for Strict Type Inference

When you need TypeScript to infer that default values are always present:

import { defineNuxtModule } from '@nuxt/kit'

interface ModuleOptions {
  apiKey: string
  baseURL: string
  timeout?: number
}

export default defineNuxtModule<ModuleOptions>().with({
  meta: {
    name: '@nuxtjs/my-api',
    configKey: 'myApi',
  },
  defaults: {
    baseURL: 'https://api.example.com',
    timeout: 5000,
  },
  setup(resolvedOptions, nuxt) {
    // resolvedOptions.baseURL is guaranteed to be string (not undefined)
    // resolvedOptions.timeout is guaranteed to be number (not undefined)
  },
})

Adding Runtime Assets

Components

import { addComponent, addComponentsDir, createResolver } from '@nuxt/kit'

export default defineNuxtModule({
  setup() {
    const { resolve } = createResolver(import.meta.url)

    // Single component
    addComponent({
      name: 'MyButton',
      filePath: resolve('./runtime/components/MyButton.vue'),
    })

    // Component directory with prefix
    addComponentsDir({
      path: resolve('./runtime/components'),
      prefix: 'My',
      pathPrefix: false,
    })
  },
})

Composables and Auto-imports

import { addImports, addImportsDir, addImportsSources, createResolver } from '@nuxt/kit'

export default defineNuxtModule({
  setup() {
    const { resolve } = createResolver(import.meta.url)

    // Single import
    addImports({
      name: 'useMyUtil',
      from: resolve('./runtime/composables/useMyUtil'),
    })

    // Directory of composables
    addImportsDir(resolve('./runtime/composables'))

    // Auto-import from packages (Nuxt 4: accepts an array + package presets)
    addImportsSources([
      { package: '@vueuse/core' }, // all of the package's documented exports
      { from: 'my-lib', imports: ['useThing', 'useOther'] },
    ])
  },
})

Keyed Composables (SSR/client key injection)

If your composable needs a stable key shared between server and client (like useState/useAsyncData), register it so Nuxt's compiler injects a unique key when called with fewer than argumentLength args:

export default defineNuxtModule({
  setup(options, nuxt) {
    const { resolve } = createResolver(import.meta.url)
    nuxt.options.optimization.keyedComposables.push({
      name: 'useMyState',
      source: resolve('./runtime/composables/state'), // exact source file (no barrels)
      argumentLength: 2,
    })
  },
})

The call must be statically analyzable and imported directly from source (barrel re-exports, dynamic/indirect calls, and reassignments are not transformed).

Plugins

import { addPlugin, addPluginTemplate, createResolver } from '@nuxt/kit'

export default defineNuxtModule({
  setup(options) {
    const { resolve } = createResolver(import.meta.url)

    // Static plugin file
    addPlugin({
      src: resolve('./runtime/plugins/myPlugin'),
      mode: 'client', // 'client', 'server', or 'all'
    })

    // Dynamic plugin with generated code
    addPluginTemplate({
      filename: 'my-module-plugin.mjs',
      getContents: () => `
import { defineNuxtPlugin } from '#app/nuxt'

export default defineNuxtPlugin({
  name: 'my-module',
  setup() {
    const config = ${JSON.stringify(options)}
    // Plugin logic
  }
})`,
    })
  },
})

Server Extensions

Server Handlers

import { addServerHandler, addServerScanDir, createResolver } from '@nuxt/kit'

export default defineNuxtModule({
  setup() {
    const { resolve } = createResolver(import.meta.url)

    // Single handler
    addServerHandler({
      route: '/api/my-endpoint',
      handler: resolve('./runtime/server/api/my-endpoint'),
    })

    // Scan entire server directory (api/, routes/, middleware/, utils/)
    addServerScanDir(resolve('./runtime/server'))
  },
})

Server Composables

import { addServerImports, addServerImportsDir, createResolver } from '@nuxt/kit'

export default defineNuxtModule({
  setup() {
    const { resolve } = createResolver(import.meta.url)

    // Single server import
    addServerImports({
      name: 'useServerUtil',
      from: resolve('./runtime/server/utils/useServerUtil'),
    })

    // Server composables directory
    addServerImportsDir(resolve('./runtime/server/composables'))
  },
})

Nitro Plugin

import { addServerPlugin, createResolver } from '@nuxt/kit'

export default defineNuxtModule({
  setup() {
    const { resolve } = createResolver(import.meta.url)
    addServerPlugin(resolve('./runtime/server/plugin'))
  },
})
// runtime/server/plugin.ts
import { defineNitroPlugin } from 'nitropack/runtime'

export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('request', (event) => {
    console.log('Request:', event.path)
  })
})

Templates and Virtual Files

Generate Virtual Files

import { addTemplate, addTypeTemplate, addServerTemplate, createResolver } from '@nuxt/kit'

export default defineNuxtModule({
  setup(options, nuxt) {
    const { resolve } = createResolver(import.meta.url)

    // Client/build virtual file (accessible via #build/my-config.mjs)
    addTemplate({
      filename: 'my-config.mjs',
      getContents: () => `export default ${JSON.stringify(options)}`,
    })

    // Type declarations
    addTypeTemplate({
      filename: 'types/my-module.d.ts',
      getContents: () => `
declare module '#my-module' {
  export interface Config {
    apiKey: string
  }
}`,
    })

    // Nitro virtual file (accessible in server routes)
    addServerTemplate({
      filename: '#my-module/config.mjs',
      getContents: () => `export const config = ${JSON.stringify(options)}`,
    })
  },
})

Access Virtual Files

// In runtime plugin
// @ts-expect-error - virtual file
import config from '#build/my-config.mjs'

// In server routes
import { config } from '#my-module/config.js'

Extending Pages and Routes

import { extendPages, extendRouteRules, addRouteMiddleware, createResolver } from '@nuxt/kit'

export default defineNuxtModule({
  setup() {
    const { resolve } = createResolver(import.meta.url)

    // Add pages
    extendPages((pages) => {
      pages.push({
        name: 'my-page',
        path: '/my-route',
        file: resolve('./runtime/pages/MyPage.vue'),
      })
    })

    // Add route rules (caching, redirects, etc.)
    extendRouteRules('/api/**', {
      cache: { maxAge: 60 },
    })

    // Add middleware
    addRouteMiddleware({
      name: 'my-middleware',
      path: resolve('./runtime/middleware/myMiddleware'),
      global: true,
    })
  },
})

Module Dependencies

Declare dependencies on other modules with version constraints:

export default defineNuxtModule({
  meta: {
    name: 'my-module',
  },
  moduleDependencies: {
    '@nuxtjs/tailwindcss': {
      version: '>=6.0.0',
      // Set defaults (user can override)
      defaults: {
        exposeConfig: true,
      },
      // Force specific options
      overrides: {
        viewer: false,
      },
    },
    '@nuxtjs/i18n': {
      optional: true, // Won't fail if not installed
      defaults: {
        defaultLocale: 'en',
      },
    },
  },
  setup() {
    // Dependencies are guaranteed to be set up before this runs
  },
})

Dynamic Dependencies

moduleDependencies(nuxt) {
  const deps: Record<string, any> = {
    '@nuxtjs/tailwindcss': { version: '>=6.0.0' },
  }

  if (nuxt.options.ssr) {
    deps['@nuxtjs/html-validator'] = { optional: true }
  }

  return deps
}

Lifecycle Hooks

Requires meta.name and meta.version:

export default defineNuxtModule({
  meta: {
    name: 'my-module',
    version: '1.2.0',
  },
  onInstall(nuxt) {
    // First-time setup
    console.log('Module installed for the first time')
  },
  onUpgrade(nuxt, options, previousVersion) {
    // Version upgrade migrations
    console.log(`Upgrading from ${previousVersion}`)
  },
  setup(options, nuxt) {
    // Regular setup runs every build
  },
})

Extending Configuration

export default defineNuxtModule({
  setup(options, nuxt) {
    // Add CSS
    nuxt.options.css.push('my-module/styles.css')

    // Add runtime config
    nuxt.options.runtimeConfig.public.myModule = {
      apiUrl: options.apiUrl,
    }

    // Extend Vite config
    nuxt.options.vite.optimizeDeps ||= {}
    nuxt.options.vite.optimizeDeps.include ||= []
    nuxt.options.vite.optimizeDeps.include.push('some-package')

    // Add build transpile
    nuxt.options.build.transpile.push('my-package')
  },
})

Using Hooks

export default defineNuxtModule({
  // Declarative hooks
  hooks: {
    'components:dirs': (dirs) => {
      dirs.push({ path: '~/extra' })
    },
  },

  setup(options, nuxt) {
    // Programmatic hooks
    nuxt.hook('pages:extend', (pages) => {
      // Modify pages
    })

    nuxt.hook('imports:extend', (imports) => {
      imports.push({ name: 'myHelper', from: 'my-package' })
    })

    nuxt.hook('nitro:config', (config) => {
      // Modify Nitro config
    })

    nuxt.hook('vite:extendConfig', (config) => {
      // Modify Vite config
    })
  },
})

Path Resolution

import { createResolver, resolvePath, findPath } from '@nuxt/kit'

export default defineNuxtModule({
  async setup(options, nuxt) {
    // Resolver relative to module
    const { resolve } = createResolver(import.meta.url)

    const pluginPath = resolve('./runtime/plugin')

    // Resolve with extensions and aliases
    const entrypoint = await resolvePath('@some/package')

    // Find first existing file
    const configPath = await findPath([
      resolve('./config.ts'),
      resolve('./config.js'),
    ])
  },
})

Module Package.json

{
  "name": "my-nuxt-module",
  "version": "1.0.0",
  "type": "module",
  "exports": {
    ".": {
      "import": "./dist/module.mjs",
      "require": "./dist/module.cjs"
    }
  },
  "main": "./dist/module.cjs",
  "module": "./dist/module.mjs",
  "types": "./dist/types.d.ts",
  "files": ["dist"],
  "scripts": {
    "dev": "nuxi dev playground",
    "build": "nuxt-module-build build",
    "prepare": "nuxt-module-build build --stub"
  },
  "dependencies": {
    "@nuxt/kit": "^4.0.0"
  },
  "devDependencies": {
    "@nuxt/module-builder": "latest",
    "nuxt": "^4.0.0"
  }
}

Disabling Modules

Users can disable a module via config key:

// nuxt.config.ts
export default defineNuxtConfig({
  // Disable entirely
  myModule: false,

  // Or with options
  myModule: {
    enabled: false,
  },
})

Development Workflow

  1. Create module: npx nuxi init -t module my-module
  2. Develop: npm run dev (runs playground)
  3. Build: npm run build
  4. Test: npm run test

Best Practices

  • Use createResolver(import.meta.url) for all path resolution
  • Prefix components to avoid naming conflicts
  • Make options type-safe with ModuleOptions interface
  • Use moduleDependencies instead of installModule
  • Provide sensible defaults for all options
  • Add compatibility requirements in meta.compatibility
  • Use virtual files for dynamic configuration
  • Separate client/server plugins appropriately
<!-- Source references: - https://nuxt.com/docs/4.x/api/kit/modules - https://nuxt.com/docs/4.x/api/kit/components - https://nuxt.com/docs/4.x/api/kit/autoimports - https://nuxt.com/docs/4.x/api/kit/plugins - https://nuxt.com/docs/4.x/api/kit/templates - https://nuxt.com/docs/4.x/api/kit/nitro - https://nuxt.com/docs/4.x/api/kit/pages - https://nuxt.com/docs/4.x/api/kit/resolving - https://nuxt.com/docs/4.x/guide/modules/recipes-basics - https://nuxt.com/docs/4.x/guide/modules/module-dependencies -->

Source: SKILL.md on GitHub

No alerts9d5 checks · Risk SAFE
  • Gen Agent Trust Hub9d

    This skill provides a comprehensive technical reference for the Nuxt full-stack framework, including documentation on directory structure, routing, data fetching, and deployment. No security issues or malicious patterns were detected.

  • Socket9d

    No alerts

  • Snyk9d

    Risk: LOW · No issues

  • Runlayer7mo

    8 files scanned · No issues

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at de09c7b. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 days ago.

Activeupdated last week
Other metadata
metadata
{
  "author": "Anthony Fu",
  "version": "2026.6.22",
  "source": "Generated from https://github.com/nuxt/nuxt, scripts located at https://github.com/antfu/skills"
}

README badge

README badge for onmax/nuxt-skills/nuxt

Provides patterns and reference files for Nuxt 4.3+ projects, covering server routes with h3 validation, file-based routing, middleware, plugins, and Nuxt composables. Use this when building API endpoints, setting up pages and layouts, or working with Nuxt-specific features like auto-imports and route guards.

Generated from the current SKILL.md.

Does this skill cover Nuxt 3 or only Nuxt 4+?
This skill targets Nuxt 4.3+ only. It includes patterns specific to Nuxt 4, such as typed router and getRouterParam, and does not cover Nuxt 2 or 3 conventions.
Does this skill include guidance for h3 and Nitro?
Yes. The skill covers h3 v1 helpers (validation, WebSocket, SSE) and nitropack v2 patterns for server routes and middleware.
Does this cover UI components like buttons or forms?
No. The skill covers Nuxt-specific components (NuxtLink, NuxtImg, NuxtTime) but delegates UI component libraries to the separate nuxt-ui skill.
Can I use this skill for creating Nuxt modules?
No. For module development, use the nuxt-modules skill instead. This skill focuses on building applications with Nuxt 4+.
Does this skill include database or content management guidance?
No. For databases, use the nuxthub skill; for content-driven sites, use the nuxt-content skill.

Generated from the current SKILL.md. These answers refresh after source changes.