Skip to content
← All questions
Intermediate

How do Nuxt modules work and when would you create one?

NuxtArchitecture

Nuxt modules are plugins that run at build time and extend the framework. They can register components, composables, server routes, plugins, and modify the Nuxt/Vite config. The ecosystem is full of them: @nuxt/ui, @nuxt/image, @nuxtjs/i18n, @pinia/nuxt.

Using modules

Install and add to nuxt.config.ts:

ts
export default defineNuxtConfig({
  modules: [
    '@nuxt/ui',
    '@pinia/nuxt',
    ['@nuxt/image', { provider: 'cloudinary' }]
  ]
})

That's it. The module's components, composables, and plugins become available automatically.

Creating a module

Use defineNuxtModule with @nuxt/kit helpers:

ts
// modules/analytics/index.ts
import { defineNuxtModule, addPlugin, createResolver } from '@nuxt/kit'

export interface ModuleOptions {
  trackingId: string
  enabled?: boolean
}

export default defineNuxtModule<ModuleOptions>({
  meta: {
    name: 'analytics',
    configKey: 'analytics'
  },
  defaults: {
    enabled: true
  },
  setup(options, nuxt) {
    if (!options.enabled) return

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

    nuxt.options.runtimeConfig.public.analyticsId = options.trackingId

    addPlugin({
      src: resolve('./runtime/plugin'),
      mode: 'client'
    })
  }
})
ts
// modules/analytics/runtime/plugin.ts
export default defineNuxtPlugin((nuxtApp) => {
  const config = useRuntimeConfig()

  nuxtApp.hook('page:finish', () => {
    trackPageView(config.public.analyticsId)
  })
})
ts
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['~/modules/analytics'],
  analytics: {
    trackingId: 'UA-123456'
  }
})

What modules can register

@nuxt/kit helperRegisters
addComponentA global component
addComponentsDirA directory of components
addImportsAn auto-imported composable/utility
addImportsDirA directory of auto-imports
addPluginA client/server/universal plugin
addServerHandlerA Nitro server route
ts
setup(options, nuxt) {
  const { resolve } = createResolver(import.meta.url)

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

  addImports({
    name: 'useTracking',
    from: resolve('./runtime/composables/useTracking')
  })

  addServerHandler({
    route: '/api/analytics',
    handler: resolve('./runtime/server/api/analytics')
  })
}

Module directory structure

modules/analytics/
├── index.ts              ← module definition (build time)
└── runtime/
    ├── components/       ← Vue components
    ├── composables/      ← composables for auto-import
    ├── server/           ← Nitro server routes
    └── plugin.ts         ← Nuxt plugin

The runtime/ directory contains code that runs in the app. Everything outside it runs only at build time.

When to create a module vs a plugin vs a composable

NeedUse
Add build-time config, register components/routes/pluginsModule
Run code at app startup (client or server)Plugin
Reusable logic for individual componentsComposable
Share functionality across multiple Nuxt projectsModule (publishable to npm)
One-off project-specific integrationPlugin or composable is usually enough

Released under the MIT License.