skip to content

In Nuxt 4, how do you add a module in `nuxt.config.ts` and pass it options, and when does that configuration run?

level: juniorimportance: should knowfreq 38%

answer

  1. one array, three entry shapes
  2. a top-level key named by the module
  3. inline beats config key beats defaults
  4. local modules folder, alphabetical
  5. Node at dev and build time

basics

~20 s

List it in the modules array of nuxt.config.ts as a package name, a path, a [name, options] tuple or an inline function, or set options under the module's config key. The file runs in Node when nuxt dev or nuxt build starts.

solid answer

~40 s

`nuxt.config.ts` at the project root default-exports `defineNuxtConfig({...})`, which is available without an import. Modules go in its `modules` array: a package name such as `'nuxt-pageview-analytics'`, a path like `'~~/modules/audit'`, a tuple `['nuxt-pageview-analytics', { endpoint: '/api/_pageview' }]`, or an inline function. A module built with `defineNuxtModule` also reads a top-level key named by its `meta.configKey`, for example `pageviewAnalytics: { ... }`. Nuxt merges the tuple's inline options over that key, and both over the module's `defaults`. Modules listed in the array run in order, then files in the root `modules/` directory run alphabetically without being listed. The whole file is evaluated in Node when `nuxt dev`, `nuxt build` or `nuxt generate` starts; the built server never reads it, so values it computes from `process.env` are frozen at build time.

code

ts · 8 lines
ts
// nuxt.config.ts
export default defineNuxtConfig({
  modules: [
    'nuxt-pageview-analytics',                       // package name
    ['~~/modules/audit', { level: 'warn' }],          // path + inline options
  ],
  pageviewAnalytics: { endpoint: '/api/_pageview' }, // the module's configKey
})

go deeper

for a junior

Recall the modules array and its entry shapes, the module's own config key for options, and that nuxt.config.ts is evaluated at build time in Node.

for a middle

Explain how inline options, the config key and defaults merge, how local modules in modules/ are ordered, and what $production and --envName change.

for a senior

Show judgment about what belongs in build-time config versus runtime config, and how module order and one-time installation affect modules that configure each other.

for a principal

Treat nuxt.config.ts as a build contract shared by several teams: which modules are approved, who owns their options, and how environments are expressed without drift.

## What `nuxt.config.ts` is `nuxt.config.ts` sits at the project root and default-exports the result of `defineNuxtConfig`, a helper that is available without an import and gives the object its types. Nuxt loads the file when a CLI command starts: `nuxt dev`, `nuxt build` or `nuxt generate`. It runs **in Node, at build time**. The built server in `.output/` never loads it again, and nothing in it is shipped to the browser unless an option explicitly exposes a value (the `public` part of `runtimeConfig`, for instance). Consequences worth saying out loud: - A `.env` file in the project root is loaded for those CLI commands, so `process.env` values are visible inside `nuxt.config.ts` while building. - Anything the file computes, such as `process.env.SOMETHING`, is a build-time value. - Per-environment blocks let one file carry differences: `$production`, `$development`, and named environments under `$env` selected with `nuxt build --envName staging`. ## Registering a module A **module** is a function Nuxt calls during that build-time start-up. It can change options, add plugins, components, composables and server routes, or hook into the build. You register it in the `modules` array, which accepts four shapes: | Entry | Example | Use | |---|---|---| | package name | `'nuxt-pageview-analytics'` | a module installed from npm | | path | `'~~/modules/audit'` | a module file in your repository | | tuple | `['nuxt-pageview-analytics', { endpoint: '/api/_pageview' }]` | a module plus inline options | | inline function | `(options, nuxt) => { ... }` | a tiny one-off tweak | Files in the root `modules/` directory, matching `modules/*.ts` or `modules/*/index.ts`, are registered automatically and do not need an entry. ## Passing options A module defined with `defineNuxtModule` declares `meta.configKey` (which falls back to `meta.name`). Nuxt then reads options from three places and merges them with `defu`, the first source winning on conflicts: 1. **Inline options** from a tuple entry in `modules`. 2. **The config key**: a top-level key in `nuxt.config.ts`, such as `pageviewAnalytics: { endpoint: '/api/_pageview' }`. 3. **The module's `defaults`**, an object or a function of the Nuxt instance. The config-key style is the usual one for published modules because the module can type it, so the editor completes and checks your options. ## Order and duplicates - Modules in the `modules` array run **sequentially in array order**. A module that changes another module's options must come before it, or declare the relationship in `moduleDependencies`. - Then modules from the `modules/` directory run in **alphabetical order**; prefix directory names with numbers (`1.first-module/`) to control it. - A module defined with `defineNuxtModule` and a `meta.name` or config key installs **only once**, even if two entries or another module ask for it. - If the module declares `meta.compatibility` and your Nuxt version does not satisfy it, Nuxt skips the module with a warning rather than failing, unless `experimental.enforceModuleCompatibility` is on. ## Example for an analytics setup ```ts // nuxt.config.ts export default defineNuxtConfig({ modules: ['nuxt-pageview-analytics'], pageviewAnalytics: { endpoint: '/api/_pageview' }, runtimeConfig: { analyticsApiKey: '', public: { siteUrl: 'http://localhost:3000' }, }, $production: { pageviewAnalytics: { sampleRate: 0.5 }, }, }) ``` The module's options live under its config key. The API key and the site URL live in `runtimeConfig`, because they differ per deployment and must be settable without a rebuild. ## What a module can and cannot do from here Because modules run during this build-time start-up, they are the right tool for anything that shapes the app before it exists: - adding runtime files: Nuxt plugins, auto-imported composables, components, Nitro server routes; - changing options such as `nuxt.options.runtimeConfig`, route rules or build settings; - hooking into build events, for example to extend the page list. They are the wrong tool for per-request or per-visitor logic. That belongs in the runtime files a module adds, not in its `setup`, which has finished long before the first visitor arrives. A module that needs to know something at runtime must leave it behind in `runtimeConfig` or in a generated file. ## Mistakes interviewers listen for - Expecting `nuxt.config.ts` to run when the production server starts, and reading secrets from `process.env` there. - Passing the same option both inline and under the config key, then being surprised the inline value wins. - Assuming a local module in `modules/` runs before the modules listed in the array; listed modules always run first. - Expecting a module's options to be readable in components without the module copying them into runtime config. - Putting functions or class instances into `runtimeConfig` from the config file; it is serialised on the way to the server.

  • Where do options passed to a module end up at runtime?
    Nowhere, unless the module puts them somewhere. Module options exist only while Nuxt builds. A module that needs a value at runtime copies it into `runtimeConfig` (the `public` part only if the browser may see it), or generates a template file, and its runtime code reads that.
  • How do you make production-only config without a separate file?
    Use the `$production` block in `nuxt.config.ts`; its options apply when Nuxt builds for production. `$development` applies in `nuxt dev`, and named blocks under `$env` apply when a command runs with `--envName`, for example `nuxt build --envName staging`. All are resolved at build time.

saying these in an interview costs you the question

  • nuxt.config.ts is re-read by the production server every time it starts.
  • Module options set in nuxt.config are automatically readable in components at runtime.
  • Options under the module's config key override inline tuple options.
  • Every module in the modules/ directory must also be listed in the modules array.
  • Modules run in parallel, so their order in the array does not matter.