In Nuxt 4, how do you add a module in `nuxt.config.ts` and pass it options, and when does that configuration run?
answer
- one array, three entry shapes
- a top-level key named by the module
- inline beats config key beats defaults
- local modules folder, alphabetical
- Node at dev and build time
basics
~20 sList 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// 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
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.
Explain how inline options, the config key and defaults merge, how local modules in modules/ are ordered, and what $production and --envName change.
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.
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.