Skip to content
← All questions
⚡ Intermediate

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

💚 Nuxt🏗️ Architecture⚡ Vite

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

See also: What are Nuxt layers? · How do auto-imports work in Nuxt? · What is the Nuxt directory structure?

References

Released under the MIT License.