Index of every page on this site: llms.txt
All skills
onmax avatar

/nuxt-modules

@2e74504 official
by Maxonmax/nuxt-skills722 stars
38
Run it yourself

Use when creating Nuxt modules: (1) Published npm modules (@nuxtjs/, nuxt-), (2) Local project modules (modules/ directory), (3) Runtime extensions (components, composables, plugins), (4) Server extensions (API routes, middleware), (5) Releasing/publishing modules to npm, (6) Setting up CI/CD workflows for modules. Provides defineNuxtModule patterns, Kit utilities, hooks, E2E testing, and release automation.

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

Nothing lands on disk. Nothing to clean up.

Fork this Skill

Edit a local copy. It keeps the author and licence.

referencesdevelopment.md

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

Module Development

Module anatomy, Kit utilities, and common patterns.

defineNuxtModule

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

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

export default defineNuxtModule<ModuleOptions>({
  meta: {
    name: '@nuxtjs/example',
    configKey: 'example',
    compatibility: { nuxt: '>=3.0.0' }
  },
  defaults: {
    apiKey: '',
    prefix: 'My'
  },
  hooks: {
    'app:error': err => console.error(err)
  },
  moduleDependencies: {
    '@nuxtjs/tailwindcss': {
      version: '>=6.0.0',
      optional: true,
      // Override nuxt.options for this module
      overrides: {},
      // Set defaults (lower priority than nuxt.options)
      defaults: {}
    }
  },
  // Or as async function (Nuxt 4.3+)
  async moduleDependencies(nuxt) {
    const needsSupport = nuxt.options.runtimeConfig.public?.feature
    return {
      '@nuxtjs/tailwindcss': needsSupport ? {} : { optional: true }
    }
  },
  setup(options, nuxt) {
    const { resolve } = createResolver(import.meta.url)
    addPlugin(resolve('./runtime/plugin'))
  }
})

User configures via configKey:

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxtjs/example'],
  example: { apiKey: 'xxx' }
})

Critical: #imports in Published Modules

Auto-imports don't work in node_modules. Runtime files must explicitly import:

// src/runtime/composables/useMyFeature.ts

// Wrong - auto-import won't work in published module
// const route = useRoute()

// Right - explicit import
import { useRoute } from '#imports'

const route = useRoute()

Adding Plugins

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

export default defineNuxtModule({
  setup(options, nuxt) {
    const { resolve } = createResolver(import.meta.url)
    addPlugin(resolve('./runtime/plugin'))
  }
})
// src/runtime/plugin.ts
import { defineNuxtPlugin } from '#imports'

export default defineNuxtPlugin((nuxtApp) => {
  return {
    provide: { myHelper: (msg: string) => console.log(msg) }
  }
})

Async plugins (Nuxt 4.3+): Lazy-load build plugins:

import { addVitePlugin, addWebpackPlugin } from '@nuxt/kit'

export default defineNuxtModule({
  async setup() {
    // Lazy-load only the bundler plugin needed
    addVitePlugin(() => import('my-plugin/vite').then(r => r.default()))
    addWebpackPlugin(() => import('my-plugin/webpack').then(r => r.default()))
  }
})

Adding Components

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

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

    // Single component
    addComponent({
      name: 'MyButton',
      filePath: resolve('./runtime/components/MyButton.vue'),
      // Custom declaration path (Nuxt 4.2+)
      declarationPath: resolve('./runtime/types/components.d.ts')
    })

    // Or entire directory with prefix
    addComponentsDir({
      path: resolve('./runtime/components'),
      prefix: 'My' // <MyButton>, <MyCard>
    })
  }
})

Adding Composables

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

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

    // Single or multiple
    addImports([
      { name: 'useAuth', from: resolve('./runtime/composables/useAuth') },
      { name: 'useUser', from: resolve('./runtime/composables/useUser') }
    ])

    // Or entire directory
    addImportsDir(resolve('./runtime/composables'))
  }
})

Adding Server Routes

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

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

    addServerHandler({
      route: '/api/_my-module/status',
      handler: resolve('./runtime/server/api/status.get')
    })
  }
})

Always prefix routes: /api/_my-module/ avoids conflicts.

Server imports (Nuxt 4.3+): Use #server alias in server files:

// runtime/server/api/users.ts
import { helper } from '#server/utils/helper'  // Clean imports

Runtime Config

export default defineNuxtModule({
  setup(options, nuxt) {
    // Public (client + server)
    nuxt.options.runtimeConfig.public.myModule = { apiUrl: options.apiUrl }

    // Private (server only)
    nuxt.options.runtimeConfig.myModule = { apiKey: options.apiKey }
  }
})

Lifecycle Hooks

export default defineNuxtModule({
  hooks: {
    'pages:extend': (pages) => {
      pages.push({ name: 'custom', path: '/custom', file: resolve('./runtime/pages/custom.vue') })
    }
  },
  setup(options, nuxt) {
    nuxt.hook('nitro:config', (nitroConfig) => {
      nitroConfig.prerender ||= {}
      nitroConfig.prerender.routes ||= []
      nitroConfig.prerender.routes.push('/my-route')
    })

    // Cleanup on close
    nuxt.hook('close', async () => {
      await cleanup()
    })
  }
})
Hook When
ready Nuxt initialized
modules:done All modules loaded
pages:extend Modify pages array
nitro:config Configure Nitro
close Nuxt shutting down

Custom Hooks

export interface ModuleHooks {
  'my-module:init': (config: MyConfig) => void
}

declare module '#app' {
  interface RuntimeNuxtHooks extends ModuleHooks {}
}

export default defineNuxtModule({
  setup(options, nuxt) {
    nuxt.hook('modules:done', async () => {
      await nuxt.callHook('my-module:init', { foo: 'bar' })
    })
  }
})

Virtual Files (Templates)

For addTemplate and addTypeTemplate development regeneration, read template dependencies before declaring dependsOn.

import { addTemplate, defineNuxtModule } from '@nuxt/kit'

export default defineNuxtModule({
  setup(options, nuxt) {
    addTemplate({
      filename: 'my-module/config.mjs',
      getContents: () => `export const config = ${JSON.stringify(options)}`
    })
  }
})

Import: import { config } from '#build/my-module/config.mjs'

Type Declarations

import { addTypeTemplate, defineNuxtModule } from '@nuxt/kit'

export default defineNuxtModule({
  setup(options, nuxt) {
    addTypeTemplate({
      filename: 'types/my-module.d.ts',
      getContents: () => `
        declare module '#app' {
          interface NuxtApp { $myHelper: (msg: string) => void }
        }
        export {}
      `
    })
  }
})

Logging & Errors

Use consola.withTag for consistent module logging:

import { consola } from 'consola'

const logger = consola.withTag('my-module')

export default defineNuxtModule({
  setup(options, nuxt) {
    logger.info('Initializing...')
    logger.warn('Deprecated option used')

    // Errors must include tag manually - consola doesn't add it
    if (!options.apiKey) {
      throw new Error('[my-module] `apiKey` option is required')
    }
  }
})

Disabling Modules

Set to false to disable (Nuxt 4.3+):

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxtjs/tailwindcss'],
  tailwindcss: false  // Disable the module
})

Disable inherited layer modules:

// nuxt.config.ts
export default defineNuxtConfig({
  extends: ['../base-layer'],
  disabledModules: ['@nuxt/image', '@sentry/nuxt/module']
})

Only works for modules from layers, not root project modules.

Local Modules

For project-specific modules:

// modules/my-local-module/index.ts
import { defineNuxtModule } from '@nuxt/kit'

export default defineNuxtModule({
  meta: { name: 'my-local-module' },
  setup(options, nuxt) {
    // Module logic
  }
})
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['./modules/my-local-module']
})

Head Management (Nuxt 4.2+)

For modules that need to set global head elements:

import { setGlobalHead } from '@nuxt/kit'

export default defineNuxtModule({
  setup(options, nuxt) {
    setGlobalHead({
      title: 'My Module',
      meta: [{ name: 'description', content: 'Description' }],
      link: [{ rel: 'icon', href: '/favicon.ico' }]
    })
  }
})

Module Resolution (Nuxt 4.2+)

Resolve modules with custom extensions:

import { resolveModule } from '@nuxt/kit'

export default defineNuxtModule({
  async setup(options, nuxt) {
    const modulePath = await resolveModule('my-module', {
      extensions: ['.mjs', '.js', '.ts']
    })
  }
})

Quick Reference

Task Kit Function
Add plugin addPlugin()
Add component addComponent() / addComponentsDir()
Add composable addImports() / addImportsDir()
Add server route addServerHandler()
Add server utils addServerImports()
Virtual file addTemplate() / addServerTemplate()
Add types addTypeTemplate()
Add CSS nuxt.options.css.push()
Set global head setGlobalHead()
Resolve module resolveModule()

Resources

Source: SKILL.md on GitHub

skilld matched fixed text patterns in SKILL.md and file names. Patterns miss obfuscated code.

skilld run checks every file with the same patterns. It asks for approval before it loads a Skill with a behavior marked Needs approval.

No alerts10d5 checks · Risk SAFE
  • Gen Agent Trust Hub10d

    This skill is a safe and high-quality guide for Nuxt module development. It correctly implements security best practices, such as recommending OIDC for CI/CD workflows and providing safe patterns for template generation.

  • Socket10d

    No alerts

  • Snyk10d

    Risk: LOW · No issues

  • Runlayer8mo

    4 files scanned · No issues

  • ZeroLeaks6mo

    Score: 93/100 · 2 sections analyzed

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

Last checked against GitHub last week.

Activeupdated 2 weeks ago

README badge

README badge for onmax/nuxt-skills/nuxt-modules

Instructs Claude on building Nuxt modules using defineNuxtModule, Kit utilities, and hooks to extend Nuxt with components, composables, plugins, server routes, and middleware. Covers published npm modules, local project modules, E2E testing patterns, and release automation workflows.

Generated from the current SKILL.md.

Does this skill cover local modules in the modules/ directory?
Yes. The skill covers published npm modules, local project modules in the modules/ directory, and inline modules defined in nuxt.config.ts.
What testing approach does this skill teach?
The skill provides E2E testing patterns and best practices via the testing-and-publishing reference, though you load it only when needed for your task.
Does this include CI/CD setup?
Yes. The skill includes copy-paste CI/CD workflow templates in the ci-workflows reference for automating module testing and release.
Can I use this skill to publish a module to npm?
Yes. The testing-and-publishing reference covers releasing and publishing modules to npm registries with @nuxtjs/ or nuxt- naming conventions.
Does this cover runtime extensions like components and composables?
Yes. The skill covers injecting components, composables, plugins, and server extensions (API routes, middleware) into user applications via the runtime/ directory.

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