Here are some common patterns used by module authors.
Nuxt configuration can be read and altered by modules. Here's an example of a module enabling an experimental feature.
import { defineNuxtModule } from '@nuxt/kit'
export default defineNuxtModule({
setup (options, nuxt) {
// We create the `experimental` object if it doesn't exist yet
nuxt.options.experimental ||= {}
nuxt.options.experimental.componentIslands = true
},
})
When you need to handle more complex configuration alterations, you should consider using defu.
Because modules aren't part of the application runtime, their options aren't either. However, in many cases, you might need access to some of these module options within your runtime code. We recommend exposing the needed config using Nuxt's runtimeConfig.
import { defineNuxtModule } from '@nuxt/kit'
import { defu } from 'defu'
export default defineNuxtModule({
setup (options, nuxt) {
nuxt.options.runtimeConfig.public.myModule = defu(nuxt.options.runtimeConfig.public.myModule, {
foo: options.foo,
})
},
})
Note that we use defu to extend the public runtime configuration the user provides instead of overwriting it.
You can then access your module options in a plugin, component, the application like any other runtime configuration:
import { useRuntimeConfig } from '@nuxt/kit'
const options = useRuntimeConfig().public.myModule
Plugins are a common way for a module to add runtime logic. You can use the addPlugin utility to register them from your module.
import { addPlugin, createResolver, defineNuxtModule } from '@nuxt/kit'
export default defineNuxtModule({
setup (options, nuxt) {
// Create resolver to resolve relative paths
const resolver = createResolver(import.meta.url)
addPlugin(resolver.resolve('./runtime/plugin'))
},
})
If your module should provide Vue components, you can use the addComponent utility to add them as auto-imports for Nuxt to resolve.
import { addComponent, createResolver, defineNuxtModule, useRuntimeConfig } from '@nuxt/kit'
export default defineNuxtModule({
setup (options, nuxt) {
const resolver = createResolver(import.meta.url)
// From the runtime directory
addComponent({
name: 'MySuperComponent', // name of the component to be used in vue templates
export: 'MySuperComponent', // (optional) if the component is a named (rather than default) export
filePath: resolver.resolve('runtime/components/MySuperComponent.vue'),
})
// From a library
addComponent({
name: 'MyAwesomeComponent', // name of the component to be used in vue templates
export: 'MyAwesomeComponent', // (optional) if the component is a named (rather than default) export
filePath: '@vue/awesome-components',
})
},
})
Alternatively, you can add an entire directory by using addComponentsDir.
import { addComponentsDir, defineNuxtModule } from '@nuxt/kit'
export default defineNuxtModule({
setup (options, nuxt) {
const resolver = createResolver(import.meta.url)
addComponentsDir({
path: resolver.resolve('runtime/components'),
})
},
})
If your module should provide composables, you can use the addImports utility to add them as auto-imports for Nuxt to resolve.
import { addImports, createResolver, defineNuxtModule } from '@nuxt/kit'
export default defineNuxtModule({
setup (options, nuxt) {
const resolver = createResolver(import.meta.url)
addImports({
name: 'useComposable', // name of the composable to be used
as: 'useComposable',
from: resolver.resolve('runtime/composables/useComposable'), // path of composable
})
},
})
Alternatively, you can add an entire directory by using addImportsDir.
import { addImportsDir, createResolver, defineNuxtModule } from '@nuxt/kit'
export default defineNuxtModule({
setup (options, nuxt) {
const resolver = createResolver(import.meta.url)
addImportsDir(resolver.resolve('runtime/composables'))
},
})
import { addServerHandler, createResolver, defineNuxtModule } from '@nuxt/kit'
export default defineNuxtModule({
setup (options, nuxt) {
const resolver = createResolver(import.meta.url)
addServerHandler({
route: '/api/_my-module/hello',
handler: resolver.resolve('./runtime/server/api/hello/index.get'),
})
},
})
You can also add a dynamic server route:
import { addServerHandler, createResolver, defineNuxtModule } from '@nuxt/kit'
export default defineNuxtModule({
setup (options, nuxt) {
const resolver = createResolver(import.meta.url)
addServerHandler({
route: '/api/_my-module/hello/:name',
handler: resolver.resolve('./runtime/server/api/hello/[name].get'),
})
},
})
/api/auth, /api/login, or /api/user may already be used by the application.If your module should provide other kinds of assets, they can also be injected. Here's a simple example module injecting a stylesheet through Nuxt's css array.
import { addPlugin, createResolver, defineNuxtModule } from '@nuxt/kit'
export default defineNuxtModule({
setup (options, nuxt) {
const resolver = createResolver(import.meta.url)
nuxt.options.css.push(resolver.resolve('./runtime/style.css'))
},
})
And a more advanced one, exposing a folder of assets through Nitro's publicAssets option:
import { createResolver, defineNuxtModule } from '@nuxt/kit'
export default defineNuxtModule({
setup (options, nuxt) {
const resolver = createResolver(import.meta.url)
nuxt.hook('nitro:config', (nitroConfig) => {
nitroConfig.publicAssets ||= []
nitroConfig.publicAssets.push({
dir: resolver.resolve('./runtime/public'),
maxAge: 60 * 60 * 24 * 365, // 1 year
})
})
},
})
If your module depends on other modules, you can specify them using the moduleDependencies option. This provides a more robust way to handle module dependencies with version constraints and configuration merging:
import { createResolver, defineNuxtModule } from '@nuxt/kit'
const resolver = createResolver(import.meta.url)
export default defineNuxtModule<ModuleOptions>({
meta: {
name: 'my-module',
},
moduleDependencies: {
'@nuxtjs/tailwindcss': {
// You can specify a version constraint for the module
version: '>=6',
// Any configuration that should override `nuxt.options`
overrides: {
exposeConfig: true,
},
// Any configuration that should be set. It will override module defaults but
// will not override any configuration set in `nuxt.options`
defaults: {
config: {
darkMode: 'class',
content: {
files: [
resolver.resolve('./runtime/components/**/*.{vue,mjs,ts}'),
resolver.resolve('./runtime/*.{mjs,js,ts}'),
],
},
},
},
},
},
setup (options, nuxt) {
// We can inject our CSS file which includes Tailwind's directives
nuxt.options.css.push(resolver.resolve('./runtime/assets/styles.css'))
},
})
moduleDependencies option replaces the deprecated installModule function and ensures proper setup order and configuration merging.