@nuxt/docs 4.0.3 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -51,7 +51,7 @@ The [`<NuxtLink>`](/docs/api/components/nuxt-link) component links pages between
51
51
 
52
52
  When a [`<NuxtLink>`](/docs/api/components/nuxt-link) enters the viewport on the client side, Nuxt will automatically prefetch components and payload (generated pages) of the linked pages ahead of time, resulting in faster navigation.
53
53
 
54
- ```vue [pages/app.vue]
54
+ ```vue [pages/index.vue]
55
55
  <template>
56
56
  <header>
57
57
  <nav>
@@ -178,8 +178,8 @@ const date = useLocaleDate(new Date('2016-10-26'))
178
178
  <p>{{ date }}</p>
179
179
  <label for="locale-chooser">Preview a different locale</label>
180
180
  <select id="locale-chooser" v-model="locale">
181
- <option v-for="locale of locales" :key="locale" :value="locale">
182
- {{ locale }}
181
+ <option v-for="loc of locales" :key="loc" :value="loc">
182
+ {{ loc }}
183
183
  </option>
184
184
  </select>
185
185
  </div>
@@ -75,6 +75,27 @@ export default defineNuxtConfig({
75
75
 
76
76
  Nuxt uses [unjs/c12](https://c12.unjs.io) and [unjs/giget](https://giget.unjs.io) for extending remote layers. Check the documentation for more information and all available options.
77
77
 
78
+ ## Layer Priority
79
+
80
+ When using multiple layers, it's important to understand how they override each other:
81
+
82
+ 1. **Layers in `extends`** - earlier entries have higher priority (first overrides second)
83
+ 2. **Auto-scanned local layers** from `~~/layers` directory in alphabetical order (Z overrides A)
84
+ 3. **Your project** has the highest priority in the stack - it will always override other layers
85
+
86
+ ```ts [nuxt.config.ts]
87
+ export default defineNuxtConfig({
88
+ extends: [
89
+ '../base', // Highest priority (among extends)
90
+ '@my-themes/awesome', // Medium priority
91
+ 'github:my-themes/awesome#v1', // Lower priority
92
+ ]
93
+ // Your project has the highest priority
94
+ })
95
+ ```
96
+
97
+ This means if multiple layers define the same component, configuration, or file, the one with higher priority will be used.
98
+
78
99
  ::read-more{to="/docs/guide/going-further/layers"}
79
100
  Read more about layers in the **Layer Author Guide**.
80
101
  ::
@@ -54,10 +54,28 @@ We currently ship an environment for unit testing code that needs a [Nuxt](https
54
54
  2. Create a `vitest.config.ts` with the following content:
55
55
 
56
56
  ```ts twoslash
57
- import { defineVitestConfig } from '@nuxt/test-utils/config'
57
+ import { defineConfig } from 'vitest/config'
58
+ import { defineVitestProject } from '@nuxt/test-utils/config'
58
59
 
59
- export default defineVitestConfig({
60
- // any custom Vitest config you require
60
+ export default defineConfig({
61
+ test: {
62
+ projects: [
63
+ {
64
+ test: {
65
+ name: 'unit',
66
+ include: ['test/{e2e,unit}/*.{test,spec}.ts'],
67
+ environment: 'node',
68
+ },
69
+ },
70
+ await defineVitestProject({
71
+ test: {
72
+ name: 'nuxt',
73
+ include: ['test/nuxt/*.{test,spec}.ts'],
74
+ environment: 'nuxt',
75
+ },
76
+ }),
77
+ ],
78
+ },
61
79
  })
62
80
  ```
63
81
 
@@ -72,24 +90,16 @@ It is possible to set environment variables for testing by using the `.env.test`
72
90
 
73
91
  ### Using a Nuxt Runtime Environment
74
92
 
75
- By default, `@nuxt/test-utils` will not change your default Vitest environment, so you can do fine-grained opt-in and run Nuxt tests together with other unit tests.
93
+ Using [Vitest projects](https://vitest.dev/guide/projects.html#test-projects), you have fine-grained control over which tests run in which environment:
76
94
 
77
- You can opt in to a Nuxt environment by adding `.nuxt.` to the test file's name (for example, `my-file.nuxt.test.ts` or `my-file.nuxt.spec.ts`) or by adding `@vitest-environment nuxt` as a comment directly in the test file.
95
+ - **Unit tests**: Place regular unit tests in `test/unit/` - these run in a Node environment for speed
96
+ - **Nuxt tests**: Place tests that rely on the Nuxt runtime environment in `test/nuxt/` - these will run within a Nuxt runtime environment
78
97
 
79
- ```ts twoslash
80
- // @vitest-environment nuxt
81
- import { test } from 'vitest'
98
+ #### Alternative: Simple Setup
82
99
 
83
- test('my test', () => {
84
- // ... test with Nuxt environment!
85
- })
86
- ```
87
-
88
- You can alternatively set `environment: 'nuxt'` in your Vitest configuration to enable the Nuxt environment for **all tests**.
100
+ If you prefer a simpler setup and want all tests to run in the Nuxt environment, you can use the basic configuration:
89
101
 
90
102
  ```ts twoslash
91
- // vitest.config.ts
92
- import { fileURLToPath } from 'node:url'
93
103
  import { defineVitestConfig } from '@nuxt/test-utils/config'
94
104
 
95
105
  export default defineVitestConfig({
@@ -109,7 +119,7 @@ export default defineVitestConfig({
109
119
  })
110
120
  ```
111
121
 
112
- If you have set `environment: 'nuxt'` by default, you can then opt _out_ of the [default environment](https://vitest.dev/guide/environment.html#test-environment) per test file as needed.
122
+ If you're using the simple setup with `environment: 'nuxt'` by default, you can opt _out_ of the [Nuxt environment](https://vitest.dev/guide/environment.html#test-environment) per test file as needed.
113
123
 
114
124
  ```ts twoslash
115
125
  // @vitest-environment node
@@ -120,6 +130,45 @@ test('my test', () => {
120
130
  })
121
131
  ```
122
132
 
133
+ ::warning
134
+ This approach is not recommended as it creates a hybrid environment where Nuxt Vite plugins run but the Nuxt entry and `nuxtApp` are not initialized. This can lead to hard-to-debug errors.
135
+ ::
136
+
137
+ ### Organizing Your Tests
138
+
139
+ With the project-based setup, you might organize your tests as follows:
140
+
141
+ ```bash [Directory structure]
142
+ test/
143
+ ├── e2e/
144
+ │ └── ssr.test.ts
145
+ ├── nuxt/
146
+ │ ├── components.test.ts
147
+ │ └── composables.test.ts
148
+ ├── unit/
149
+ │ └── utils.test.ts
150
+ ```
151
+
152
+ You can of course opt for any test structure, but keeping the Nuxt runtime environment separated from Nuxt end-to-end tests is important for test stability.
153
+
154
+ #### Running Tests
155
+
156
+ With the project setup, you can run different test suites:
157
+
158
+ ```bash
159
+ # Run all tests
160
+ npx vitest
161
+
162
+ # Run only unit tests
163
+ npx vitest --project unit
164
+
165
+ # Run only Nuxt tests
166
+ npx vitest --project nuxt
167
+
168
+ # Run tests in watch mode
169
+ npx vitest --watch
170
+ ```
171
+
123
172
  ::warning
124
173
  When you run your tests within the Nuxt environment, they will be running in a [`happy-dom`](https://github.com/capricorn86/happy-dom) or [`jsdom`](https://github.com/jsdom/jsdom) environment. Before your tests run, a global Nuxt app will be initialized (including, for example, running any plugins or code you've defined in your `app.vue`).
125
174
 
@@ -555,7 +604,11 @@ Please use the options below for the `setup` method.
555
604
 
556
605
  - `setupTimeout`: The amount of time (in milliseconds) to allow for `setupTest` to complete its work (which could include building or generating files for a Nuxt application, depending on the options that are passed).
557
606
  - Type: `number`
558
- - Default: `60000`
607
+ - Default: `120000` or `240000` on windows
608
+
609
+ - `teardownTimeout`: The amount of time (in milliseconds) to allow tearing down the test environment, such as closing the browser.
610
+ - Type: `number`
611
+ - Default: `30000`
559
612
 
560
613
  #### Features
561
614
 
@@ -142,6 +142,44 @@ This is true even if you throw an error in your middleware on the server, and an
142
142
  Rendering an error page is an entirely separate page load, meaning any registered middleware will run again. You can use [`useError`](/docs/getting-started/error-handling#useerror) in middleware to check if an error is being handled.
143
143
  ::
144
144
 
145
+ ## Accessing Route in Middleware
146
+
147
+ Always use the `to` and `from` parameters in your middleware to access the next and previous routes. Avoid using the [`useRoute()`](/docs/api/composables/use-route) composable in this context altogether.
148
+ There is **no concept of a "current route" in middleware**, as middleware can abort a navigation or redirect to a different route. The `useRoute()` composable will always be inaccurate in this context.
149
+
150
+ ::warning
151
+ Sometimes, you might call a composable that uses `useRoute()` internally, which can trigger this warning even if there is no direct call in your middleware.
152
+ This leads to the **same issue as above**, so you should structure your functions to accept the route as an argument instead when they are used in middleware.
153
+ ::
154
+
155
+ ::code-group
156
+ ```ts twoslash [middleware/access-route.ts]
157
+ // @errors: 2304
158
+ export default defineNuxtRouteMiddleware(to => {
159
+ // passing the route to the function to avoid calling `useRoute()` in middleware
160
+ doSomethingWithRoute(to)
161
+
162
+ // ❌ this will output a warning and is NOT recommended
163
+ callsRouteInternally()
164
+ })
165
+ ```
166
+
167
+ ```ts twoslash [utils/handle-route.ts]
168
+ // providing the route as an argument so that it can be used in middleware correctly
169
+ export function doSomethingWithRoute(route = useRoute()) {
170
+ // ...
171
+ }
172
+ ```
173
+ ```ts twoslash [utils/dont-do-this.ts]
174
+ // ❌ this function is not suitable for use in middleware
175
+ export function callsRouteInternally() {
176
+ const route = useRoute()
177
+ // ...
178
+ }
179
+ ```
180
+
181
+ ::
182
+
145
183
  ## Adding Middleware Dynamically
146
184
 
147
185
  It is possible to add global or named route middleware manually using the [`addRouteMiddleware()`](/docs/api/utils/add-route-middleware) helper function, such as from within a plugin.
@@ -337,7 +337,7 @@ You may define a path matcher, if you have a more complex pattern than can be ex
337
337
 
338
338
  #### `props`
339
339
 
340
- Allows accessing the route `params` as props passed to the page component. See[the `vue-router` docs](https://router.vuejs.org/guide/essentials/passing-props) for more information.
340
+ Allows accessing the route `params` as props passed to the page component. See [the `vue-router` docs](https://router.vuejs.org/guide/essentials/passing-props) for more information.
341
341
 
342
342
  ### Typing Custom Metadata
343
343
 
@@ -27,6 +27,10 @@ modules[]=nuxt-security
27
27
 
28
28
  If present, the properties in the `nuxt.config` file will overwrite the properties in `.nuxtrc` file.
29
29
 
30
+ ::note
31
+ Nuxt automatically adds a `setups` section to track module installation and upgrade state. This is used internally for [module lifecycle hooks](/docs/api/kit/modules#using-lifecycle-hooks-for-module-installation-and-upgrade) and should not be modified manually.
32
+ ::
33
+
30
34
  ::read-more{to="/docs/api/configuration/nuxt-config"}
31
35
  Discover all the available options in the **Nuxt configuration** documentation.
32
36
  ::
@@ -637,3 +637,36 @@ export default defineNuxtConfig({
637
637
  }
638
638
  })
639
639
  ```
640
+
641
+ ## entryImportMap
642
+
643
+ By default, Nuxt improves chunk stability by using an import map to resolve the entry chunk of the bundle.
644
+
645
+ This injects an import map at the top of your `<head>` tag:
646
+
647
+ ```html
648
+ <script type="importmap">{"imports":{"#entry":"/_nuxt/DC5HVSK5.js"}}</script>
649
+ ```
650
+
651
+ Within the script chunks emitted by Vite, imports will be from `#entry`. This means that changes to the entry will not invalidate chunks which are otherwise unchanged.
652
+
653
+ ::note
654
+ Nuxt smartly disables this feature if you have configured `vite.build.target` to include a browser that doesn't support import maps, or if you have configured `vite.build.rollupOptions.output.entryFileNames` to a value that does not include `[hash]`.
655
+ ::
656
+
657
+ If you need to disable this feature you can do so:
658
+
659
+ ```ts twoslash [nuxt.config.ts]
660
+ export default defineNuxtConfig({
661
+ experimental: {
662
+ entryImportMap: false
663
+ },
664
+ // or, better, simply tell vite your desired target
665
+ // which nuxt will respect
666
+ vite: {
667
+ build: {
668
+ target: 'safari13'
669
+ },
670
+ },
671
+ })
672
+ ```
@@ -177,6 +177,23 @@ export default defineNuxtModule({
177
177
  defaults: {},
178
178
  // Shorthand sugar to register Nuxt hooks
179
179
  hooks: {},
180
+ // Configuration for other modules - this does not ensure the module runs before
181
+ // your module, but it allows you to change the other module's configuration before it runs
182
+ moduleDependencies: {
183
+ 'some-module': {
184
+ // You can specify a version constraint for the module. If the user has a different
185
+ // version installed, Nuxt will throw an error on startup.
186
+ version: '>=2',
187
+ // By default moduleDependencies will be added to the list of modules to be installed
188
+ // by Nuxt unless `optional` is set.
189
+ optional: true,
190
+ // Any configuration that should override `nuxt.options`.
191
+ overrides: {},
192
+ // Any configuration that should be set. It will override module defaults but
193
+ // will not override any configuration set in `nuxt.options`.
194
+ defaults: {}
195
+ }
196
+ },
180
197
  // The function holding your module logic, it can be asynchronous
181
198
  setup(moduleOptions, nuxt) {
182
199
  // ...
@@ -763,6 +780,38 @@ Nuxt Modules should provide an explicit prefix for any exposed configuration, pl
763
780
 
764
781
  Ideally, you should prefix them with your module's name (e.g. if your module is called `nuxt-foo`, expose `<FooButton>` and `useFooBar()` and **not** `<Button>` and `useBar()`).
765
782
 
783
+ #### Use Lifecycle Hooks for One-Time Setup
784
+
785
+ When your module needs to perform one-time setup tasks (like generating configuration files, setting up databases, or installing dependencies), use lifecycle hooks instead of running the logic in your main `setup` function.
786
+
787
+ ```ts
788
+ import { addServerHandler, defineNuxtModule } from 'nuxt/kit'
789
+ import semver from 'semver'
790
+
791
+ export default defineNuxtModule({
792
+ meta: {
793
+ name: 'my-database-module',
794
+ version: '1.0.0',
795
+ },
796
+ async onInstall (nuxt) {
797
+ // One-time setup: create database schema, generate config files, etc.
798
+ await generateDatabaseConfig(nuxt.options.rootDir)
799
+ },
800
+ async onUpgrade (options, nuxt, previousVersion) {
801
+ // Handle version-specific migrations
802
+ if (semver.lt(previousVersion, '1.0.0')) {
803
+ await migrateLegacyData()
804
+ }
805
+ },
806
+ setup (options, nuxt) {
807
+ // Regular setup logic that runs on every build
808
+ addServerHandler({ /* ... */ })
809
+ },
810
+ })
811
+ ```
812
+
813
+ This pattern prevents unnecessary work on every build and provides a better developer experience. See the [lifecycle hooks documentation](/docs/api/kit/modules#using-lifecycle-hooks-for-module-installation-and-upgrade) for more details.
814
+
766
815
  #### Be TypeScript Friendly
767
816
 
768
817
  Nuxt has first-class TypeScript integration for the best developer experience.
@@ -15,15 +15,16 @@ export default defineNuxtConfig({})
15
15
 
16
16
  Additionally, certain other files in the layer directory will be auto-scanned and used by Nuxt for the project extending this layer.
17
17
 
18
- - [`components/*`](/docs/guide/directory-structure/components) - Extend the default components
19
- - [`composables/*`](/docs/guide/directory-structure/composables) - Extend the default composables
20
- - [`layouts/*`](/docs/guide/directory-structure/layouts) - Extend the default layouts
21
- - [`pages/*`](/docs/guide/directory-structure/pages) - Extend the default pages
22
- - [`plugins/*`](/docs/guide/directory-structure/plugins) - Extend the default plugins
18
+ - [`app/components/*`](/docs/guide/directory-structure/components) - Extend the default components
19
+ - [`app/composables/*`](/docs/guide/directory-structure/composables) - Extend the default composables
20
+ - [`app/layouts/*`](/docs/guide/directory-structure/layouts) - Extend the default layouts
21
+ - [`app/middleware/*`](/docs/guide/directory-structure/middleware) - Extend the default middleware
22
+ - [`app/pages/*`](/docs/guide/directory-structure/pages) - Extend the default pages
23
+ - [`app/plugins/*`](/docs/guide/directory-structure/plugins) - Extend the default plugins
24
+ - [`app/utils/*`](/docs/guide/directory-structure/utils) - Extend the default utils
25
+ - [`app/app.config.ts`](/docs/guide/directory-structure/app-config) - Extend the default app config
23
26
  - [`server/*`](/docs/guide/directory-structure/server) - Extend the default server endpoints & middleware
24
- - [`utils/*`](/docs/guide/directory-structure/utils) - Extend the default utils
25
27
  - [`nuxt.config.ts`](/docs/guide/directory-structure/nuxt-config)- Extend the default nuxt config
26
- - [`app.config.ts`](/docs/guide/directory-structure/app-config) - Extend the default app config
27
28
 
28
29
  ## Basic Example
29
30
 
@@ -57,7 +58,7 @@ Additionally, certain other files in the layer directory will be auto-scanned an
57
58
  })
58
59
  ```
59
60
 
60
- ```vue [base/components/BaseComponent.vue]
61
+ ```vue [base/app/components/BaseComponent.vue]
61
62
  <template>
62
63
  <h1>Extending Components is Fun!</h1>
63
64
  </template>
@@ -65,6 +66,29 @@ Additionally, certain other files in the layer directory will be auto-scanned an
65
66
 
66
67
  ::
67
68
 
69
+ ## Layer Priority
70
+
71
+ When extending from multiple layers, it's important to understand the priority order:
72
+
73
+ 1. **Layers in `extends`** - earlier entries have higher priority (first overrides second)
74
+ 2. **Auto-scanned local layers** from `~~/layers` directory in alphabetical order (Z overrides A)
75
+ 3. **Your project** has the highest priority in the stack - it will always override other layers
76
+
77
+ For example:
78
+
79
+ ```ts [nuxt.config.ts]
80
+ export default defineNuxtConfig({
81
+ extends: [
82
+ './layers/base', // Highest priority (among extends)
83
+ './layers/theme', // Medium priority
84
+ './layers/custom' // Lower priority
85
+ ]
86
+ // Your project has the highest priority
87
+ })
88
+ ```
89
+
90
+ If you also have auto-scanned layers like `~~/layers/a` and `~~/layers/z`, the complete override order would be: `base` > `theme` > `custom` > `z` > `a` > your project.
91
+
68
92
  ## Starter Template
69
93
 
70
94
  To get started you can initialize a layer with the [nuxt/starter/layer template](https://github.com/nuxt/starter/tree/layer). This will create a basic structure you can build upon. Execute this command within the terminal to get started:
@@ -194,7 +218,7 @@ const currentDir = dirname(fileURLToPath(import.meta.url))
194
218
 
195
219
  export default defineNuxtConfig({
196
220
  css: [
197
- join(currentDir, './assets/main.css')
221
+ join(currentDir, './app/assets/main.css')
198
222
  ]
199
223
  })
200
224
  ```
@@ -49,7 +49,8 @@ You may need to update the config below with a path to your web browser. For mor
49
49
  "request": "launch",
50
50
  "name": "client: chrome",
51
51
  "url": "http://localhost:3000",
52
- "webRoot": "${workspaceFolder}"
52
+ // this should point to your Nuxt `srcDir`, which is `app` by default
53
+ "webRoot": "${workspaceFolder}/app"
53
54
  },
54
55
  {
55
56
  "type": "node",
@@ -12,6 +12,12 @@ links:
12
12
  Within the template of a Vue component, you can access the route using `$route`.
13
13
  ::
14
14
 
15
+ The `useRoute` composable is a wrapper around the identically named composable from `vue-router`, providing access to the current route in a Nuxt application.
16
+
17
+ The key difference is that in Nuxt, the composable ensures that the route is updated **only after** the page content has changed after navigation.
18
+ In contrast, the `vue-router` version updates the route **immediately**, which can lead to synchronization issues between different parts of the template
19
+ that rely on the route metadata, for example.
20
+
15
21
  ## Example
16
22
 
17
23
  In the following example, we call an API via [`useFetch`](/docs/api/composables/use-fetch) using a dynamic page parameter - `slug` - as part of the URL.
@@ -45,8 +51,37 @@ Apart from dynamic parameters and query parameters, `useRoute()` also provides t
45
51
  - `path`: encoded pathname section of the URL
46
52
  - `redirectedFrom`: route location that was attempted to access before ending up on the current route location
47
53
 
48
- ::note
49
- Browsers don't send [URL fragments](https://url.spec.whatwg.org/#concept-url-fragment) (for example `#foo`) when making requests. So using `route.fullPath` in your template can trigger hydration issues because this will include the fragment on client but not the server.
54
+ ## Common Pitfalls
55
+
56
+ ### Route Synchronization Issues
57
+
58
+ It’s important to use the `useRoute()` composable from Nuxt rather than the one from `vue-router` to avoid synchronization issues during page navigation.
59
+ Importing `useRoute` directly from `vue-router` bypasses Nuxt's implementation.
60
+
61
+ ```ts twoslash
62
+ // ❌ do not use `useRoute` from `vue-router`
63
+ // @errors: 2300
64
+ import { useRoute } from 'vue-router'
65
+ // ✅ use Nuxt's `useRoute` composable
66
+ import { useRoute } from '#app'
67
+ ```
68
+
69
+ ### Calling `useRoute` in Middleware
70
+
71
+ Using `useRoute` in middleware is not recommended because it can lead to unexpected behavior.
72
+ There is no concept of a "current route" in middleware.
73
+ The `useRoute()` composable should only be used in the setup function of a Vue component or in a Nuxt plugin.
74
+
75
+ ::warning
76
+ This applies to any composable that uses `useRoute()` internally too.
50
77
  ::
51
78
 
79
+ ::read-more{to="/docs/4.x/guide/directory-structure/app/middleware"}
80
+ Read more about accessing the route in the middleware section.
81
+ ::
82
+
83
+ ### Hydration Issues with `route.fullPath`
84
+
85
+ Browsers don't send [URL fragments](https://url.spec.whatwg.org/#concept-url-fragment) (for example `#foo`) when making requests. So using `route.fullPath` to affect the template can trigger hydration issues because this will include the fragment on client but not the server.
86
+
52
87
  :read-more{icon="i-simple-icons-vuedotjs" to="https://router.vuejs.org/api/type-aliases/RouteLocationNormalizedLoaded.html"}
@@ -10,7 +10,7 @@ links:
10
10
 
11
11
  <!--init-cmd-->
12
12
  ```bash [Terminal]
13
- npm create nuxt@latest [DIR] [--cwd=<directory>] [-t, --template] [-f, --force] [--offline] [--preferOffline] [--no-install] [--gitInit] [--shell] [--packageManager]
13
+ npm create nuxt@latest [DIR] [--cwd=<directory>] [-t, --template] [-f, --force] [--offline] [--preferOffline] [--no-install] [--gitInit] [--shell] [--packageManager] [--nightly]
14
14
  ```
15
15
  <!--/init-cmd-->
16
16
 
@@ -40,6 +40,7 @@ Option | Default | Description
40
40
  `--packageManager` | | Package manager choice (npm, pnpm, yarn, bun)
41
41
  `--modules` | | Nuxt modules to install (comma separated without spaces)
42
42
  `--no-modules` | | Skip module installation prompt
43
+ `--nightly` | | Use Nuxt nightly release channel (3x or latest)
43
44
  <!--/init-opts-->
44
45
 
45
46
  ## Environment variables
@@ -62,6 +62,8 @@ export function defineNuxtModule<TOptions extends ModuleOptions> (): {
62
62
  | `defaults` | `T \| ((nuxt: Nuxt) => T)`{lang="ts"} | `false` | Default options for the module. If a function is provided, it will be called with the Nuxt instance as the first argument. |
63
63
  | `schema` | `T` | `false` | Schema for the module options. If provided, options will be applied to the schema. |
64
64
  | `hooks` | `Partial<NuxtHooks>`{lang="ts"} | `false` | Hooks to be installed for the module. If provided, the module will install the hooks. |
65
+ | `onInstall` | `(nuxt: Nuxt) => Awaitable<void>`{lang="ts"} | `false` | Lifecycle hook called when the module is first installed. Requires `meta.name` and `meta.version` to be defined. |
66
+ | `onUpgrade` | `(options: T, nuxt: Nuxt, previousVersion: string) => Awaitable<void>`{lang="ts"} | `false` | Lifecycle hook called when the module is upgraded to a newer version. Requires `meta.name` and `meta.version` to be defined. |
65
67
  | `setup` | `(this: void, resolvedOptions: T, nuxt: Nuxt) => Awaitable<void \| false \| ModuleSetupInstallResult>`{lang="ts"} | `false` | Setup function for the module. If provided, the module will call the setup function. |
66
68
 
67
69
  ### Examples
@@ -171,6 +173,72 @@ export default defineNuxtModule<ModuleOptions>().with({
171
173
 
172
174
  Without using `.with()`, the `resolvedOptions` parameter would be typed as the raw `ModuleOptions` interface, where `timeout` and `retries` could be `undefined` even when defaults are provided. The `.with()` method enables TypeScript to understand that default values make those properties non-optional in the resolved options.
173
175
 
176
+ #### Using Lifecycle Hooks for Module Installation and Upgrade
177
+
178
+ You can define lifecycle hooks that run when your module is first installed or upgraded to a new version. These hooks are useful for performing one-time setup tasks, database migrations, or cleanup operations.
179
+
180
+ ::important
181
+ For lifecycle hooks to work, you **must** provide both `meta.name` and `meta.version` in your module definition. The hooks use these values to track the module's installation state in the project's `.nuxtrc` file.
182
+ ::
183
+
184
+ Lifecycle hooks run before the main `setup` function, and if a hook throws an error, it's logged but doesn't stop the build process.
185
+
186
+ **`onInstall`** runs only once when the module is first added to a project.
187
+
188
+ **`onUpgrade`** runs each time the module version increases (using semver comparison) &mdash; but only once for each version bump.
189
+
190
+ ##### Example
191
+
192
+ ```ts
193
+ import { defineNuxtModule } from '@nuxt/kit'
194
+ import semver from 'semver'
195
+
196
+ export default defineNuxtModule({
197
+ meta: {
198
+ name: 'my-awesome-module',
199
+ version: '1.2.0', // Required for lifecycle hooks
200
+ configKey: 'myAwesomeModule'
201
+ },
202
+ defaults: {
203
+ apiKey: '',
204
+ enabled: true
205
+ },
206
+
207
+ onInstall(nuxt) {
208
+ // This runs only when the module is first installed
209
+ console.log('Setting up my-awesome-module for the first time!')
210
+
211
+ // You might want to:
212
+ // - Create initial configuration files
213
+ // - Set up database schemas
214
+ // - Display welcome messages
215
+ // - Perform initial data migration
216
+ },
217
+
218
+ onUpgrade(options, nuxt, previousVersion) {
219
+ // This runs when the module is upgraded to a newer version
220
+ console.log(`Upgrading my-awesome-module from ${previousVersion} to 1.2.0`)
221
+
222
+ // You might want to:
223
+ // - Migrate configuration files
224
+ // - Update database schemas
225
+ // - Clean up deprecated files
226
+ // - Display upgrade notes
227
+
228
+ if (semver.lt(previousVersion, '1.1.0')) {
229
+ console.log('⚠️ Breaking changes in 1.1.0 - please check the migration guide')
230
+ }
231
+ },
232
+
233
+ setup(options, nuxt) {
234
+ // Regular setup logic runs on every build
235
+ if (options.enabled) {
236
+ // Configure the module
237
+ }
238
+ }
239
+ })
240
+ ```
241
+
174
242
  ## `installModule`
175
243
 
176
244
  Install specified Nuxt module programmatically. This is helpful when your module depends on other modules. You can pass the module options as an object to `inlineOptions` and they will be passed to the module's `setup` function.
@@ -0,0 +1,220 @@
1
+ ---
2
+ title: "Layers"
3
+ description: Nuxt Kit provides utilities to help you work with layers and their directory structures.
4
+ links:
5
+ - label: Source
6
+ icon: i-simple-icons-github
7
+ to: https://github.com/nuxt/nuxt/blob/main/packages/kit/src/layers.ts
8
+ size: xs
9
+ ---
10
+
11
+ Nuxt layers provide a powerful way to share and extend functionality across projects. When working with layers in modules, you often need to access directory paths from each layer. Nuxt Kit provides the `getLayerDirectories` utility to access resolved directory paths for all layers in your Nuxt application.
12
+
13
+ ## `getLayerDirectories`
14
+
15
+ Get the resolved directory paths for all layers in a Nuxt application. This function provides a structured way to access layer directories without directly accessing the private `nuxt.options._layers` property.
16
+
17
+ ### Usage
18
+
19
+ ```ts twoslash
20
+ import { defineNuxtModule, getLayerDirectories } from '@nuxt/kit'
21
+
22
+ export default defineNuxtModule({
23
+ setup() {
24
+ const layerDirs = getLayerDirectories()
25
+
26
+ // Access directories from all layers
27
+ for (const [index, layer] of layerDirs.entries()) {
28
+ console.log(`Layer ${index}:`)
29
+ console.log(` Root: ${layer.root}`)
30
+ console.log(` App: ${layer.app}`)
31
+ console.log(` Server: ${layer.server}`)
32
+ console.log(` Pages: ${layer.appPages}`)
33
+ // ... other directories
34
+ }
35
+ }
36
+ })
37
+ ```
38
+
39
+ ### Type
40
+
41
+ ```ts twoslash
42
+ // @errors: 2391
43
+ import type { Nuxt } from '@nuxt/schema'
44
+ // ---cut---
45
+ function getLayerDirectories(nuxt?: Nuxt): LayerDirectories[]
46
+
47
+ interface LayerDirectories {
48
+ /** Nuxt rootDir (`/` by default) */
49
+ readonly root: string
50
+ /** Nitro source directory (`/server` by default) */
51
+ readonly server: string
52
+ /** Local modules directory (`/modules` by default) */
53
+ readonly modules: string
54
+ /** Shared directory (`/shared` by default) */
55
+ readonly shared: string
56
+ /** Public directory (`/public` by default) */
57
+ readonly public: string
58
+ /** Nuxt srcDir (`/app/` by default) */
59
+ readonly app: string
60
+ /** Layouts directory (`/app/layouts` by default) */
61
+ readonly appLayouts: string
62
+ /** Middleware directory (`/app/middleware` by default) */
63
+ readonly appMiddleware: string
64
+ /** Pages directory (`/app/pages` by default) */
65
+ readonly appPages: string
66
+ /** Plugins directory (`/app/plugins` by default) */
67
+ readonly appPlugins: string
68
+ }
69
+ ```
70
+
71
+ ### Parameters
72
+
73
+ **`nuxt`** (optional): The Nuxt instance to get layers from. If not provided, the function will use the current Nuxt context.
74
+
75
+ ### Return Value
76
+
77
+ The `getLayerDirectories` function returns an array of `LayerDirectories` objects, one for each layer in the application.
78
+
79
+ **Layer Priority Ordering**: The layers are ordered by priority, where:
80
+ - The **first layer** is the user/project layer (highest priority)
81
+ - **Earlier layers override later layers** in the array
82
+ - **Base layers appear last** in the array (lowest priority)
83
+
84
+ This ordering matches Nuxt's layer resolution system, where user-defined configurations and files take precedence over those from base layers.
85
+
86
+ **`LayerDirectories`**: An object containing the resolved directory paths for a layer.
87
+
88
+ | Property | Type | Description |
89
+ | --------------- | -------- | ------------------------------------------------------------------------------ |
90
+ | `root` | `string` | The root directory of the layer (equivalent to `rootDir`) |
91
+ | `server` | `string` | The server directory for Nitro server-side code |
92
+ | `modules` | `string` | The local modules directory |
93
+ | `shared` | `string` | The shared directory for code used by both client and server |
94
+ | `app` | `string` | The source directory of the layer (equivalent to `srcDir`) |
95
+ | `public` | `string` | The public directory for static assets |
96
+ | `appLayouts` | `string` | The layouts directory for Vue layout components |
97
+ | `appMiddleware` | `string` | The middleware directory for route middleware |
98
+ | `appPages` | `string` | The pages directory for file-based routing |
99
+ | `appPlugins` | `string` | The plugins directory for Nuxt plugins |
100
+
101
+ ### Examples
102
+
103
+ **Processing files from all layers:**
104
+
105
+ ```ts twoslash
106
+ // @errors: 2307
107
+ // ---cut---
108
+ import { defineNuxtModule, getLayerDirectories } from '@nuxt/kit'
109
+ import { resolve } from 'pathe'
110
+ import { globby } from 'globby'
111
+
112
+ export default defineNuxtModule({
113
+ async setup() {
114
+ const layerDirs = getLayerDirectories()
115
+
116
+ // Find all component files across layers
117
+ // Note: layerDirs[0] is the user layer (highest priority)
118
+ // Later layers in the array have lower priority
119
+ const componentFiles = []
120
+ for (const [index, layer] of layerDirs.entries()) {
121
+ const files = await globby('**/*.vue', {
122
+ cwd: resolve(layer.app, 'components'),
123
+ absolute: true
124
+ })
125
+ console.log(`Layer ${index} (${index === 0 ? 'user' : 'base'}):`, files.length, 'components')
126
+ componentFiles.push(...files)
127
+ }
128
+ }
129
+ })
130
+ ```
131
+
132
+ **Adding templates from multiple layers:**
133
+
134
+ ```ts twoslash
135
+ import { defineNuxtModule, getLayerDirectories, addTemplate } from '@nuxt/kit'
136
+ import { resolve, basename } from 'pathe'
137
+ import { existsSync } from 'fs'
138
+
139
+ export default defineNuxtModule({
140
+ async setup() {
141
+ const layerDirs = getLayerDirectories()
142
+
143
+ // Add a config file from each layer that has one
144
+ for (const dirs of layerDirs) {
145
+ const configPath = resolve(dirs.app, 'my-module.config.ts')
146
+ if (existsSync(configPath)) {
147
+ addTemplate({
148
+ filename: `my-module-${basename(dirs.root)}.config.ts`,
149
+ src: configPath
150
+ })
151
+ }
152
+ }
153
+ }
154
+ })
155
+ ```
156
+
157
+ **Respecting layer priority:**
158
+
159
+ ```ts twoslash
160
+ import { defineNuxtModule, getLayerDirectories } from '@nuxt/kit'
161
+ import { resolve } from 'pathe'
162
+ import { existsSync, readFileSync } from 'fs'
163
+
164
+ export default defineNuxtModule({
165
+ setup() {
166
+ const layerDirs = getLayerDirectories()
167
+
168
+ // Find the first (highest priority) layer that has a specific config file
169
+ // This respects the layer priority system
170
+ let configContent = null
171
+ for (const dirs of layerDirs) {
172
+ const configPath = resolve(dirs.app, 'my-config.json')
173
+ if (existsSync(configPath)) {
174
+ configContent = readFileSync(configPath, 'utf-8')
175
+ console.log(`Using config from layer: ${dirs.root}`)
176
+ break // Use the first (highest priority) config found
177
+ }
178
+ }
179
+
180
+ // Alternative: Collect configs from all layers, with user layer taking precedence
181
+ const allConfigs = {}
182
+ for (const dirs of layerDirs.reverse()) { // Process from lowest to highest priority
183
+ const configPath = resolve(dirs.app, 'my-config.json')
184
+ if (existsSync(configPath)) {
185
+ const config = JSON.parse(readFileSync(configPath, 'utf-8'))
186
+ Object.assign(allConfigs, config) // Later assignments override earlier ones
187
+ }
188
+ }
189
+ }
190
+ })
191
+ ```
192
+
193
+ **Checking for layer-specific directories:**
194
+
195
+ ```ts twoslash
196
+ import { defineNuxtModule, getLayerDirectories } from '@nuxt/kit'
197
+ import { existsSync } from 'fs'
198
+ import { resolve } from 'pathe'
199
+
200
+ export default defineNuxtModule({
201
+ setup() {
202
+ const layerDirs = getLayerDirectories()
203
+
204
+ // Find layers that have a specific custom directory
205
+ const layersWithAssets = layerDirs.filter(layer => {
206
+ return existsSync(resolve(layer.app, 'assets'))
207
+ })
208
+
209
+ console.log(`Found ${layersWithAssets.length} layers with assets directory`)
210
+ }
211
+ })
212
+ ```
213
+
214
+ ::note
215
+ The `getLayerDirectories` function includes caching via a WeakMap to avoid recomputing directory paths for the same layers repeatedly, improving performance when called multiple times.
216
+ ::
217
+
218
+ ::note
219
+ Directory paths returned by this function always include a trailing slash for consistency.
220
+ ::
@@ -252,7 +252,15 @@ Customize Nuxt root element tag.
252
252
 
253
253
  ### `spaLoaderAttrs`
254
254
 
255
- Customize Nuxt Nuxt SpaLoader element attributes.
255
+ Customize Nuxt SPA loading template element attributes.
256
+
257
+ - **Type**: `object`
258
+ - **Default:**
259
+ ```json
260
+ {
261
+ "id": "__nuxt-loader"
262
+ }
263
+ ```
256
264
 
257
265
  #### `id`
258
266
 
@@ -1266,7 +1274,7 @@ Inline styles when rendering HTML (currently vite only).
1266
1274
  You can also pass a function that receives the path of a Vue component and returns a boolean indicating whether to inline the styles for that component.
1267
1275
 
1268
1276
  - **Type**: `boolean`
1269
- - **Default:** `true`
1277
+ - **Default:** `(id) => id.includes('.vue')`
1270
1278
 
1271
1279
  ### `noScripts`
1272
1280
 
@@ -59,17 +59,17 @@ We commit to support each major version of Nuxt for a minimum of six months afte
59
59
 
60
60
  ### Current Packages
61
61
 
62
- The current active version of [Nuxt](https://nuxt.com) is **v3** which is available as `nuxt` on npm with the `latest` tag.
62
+ The current active version of [Nuxt](https://nuxt.com) is **v4** which is available as `nuxt` on npm with the `latest` tag.
63
63
 
64
- Nuxt 2 is in maintenance mode and is available on npm with the `2x` tag. It reached End of Life (EOL) on June 30, 2024.
64
+ Nuxt 3 will continue to receive maintenance updates (both bug fixes and backports of features from Nuxt 4) until the end of January 2026.
65
65
 
66
66
  Each active version has its own nightly releases which are generated automatically. For more about enabling the Nuxt nightly release channel, see [the nightly release channel docs](/docs/guide/going-further/nightly-release-channel).
67
67
 
68
- Release | | Initial release | End Of Life | Docs
69
- ----------------------------------------|---------------------------------------------------------------------------------------------------|-----------------|--------------|-------
68
+ Release | | Initial release | End Of Life | Docs
69
+ -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | ---------------------------- | ---------------------------------------
70
70
  **5.x** (scheduled) | | Q4 2025 (estimated) | TBA | &nbsp;
71
- **4.x** (scheduled) | | 2025-06-30 (planned) | 6 months after 5.x release | &nbsp;
72
- **3.x** (stable) | <a href="https://npmjs.com/package/nuxt"><img alt="Nuxt latest 3.x version" src="https://flat.badgen.net/npm/v/nuxt?label=" class="not-prose"></a> | 2022-11-16 | 2025-12-31 (TBC) | [nuxt.com](/docs)
71
+ **4.x** (stable) | <a href="https://www.npmjs.com/package/nuxt?activeTab=versions"><img alt="Nuxt latest version" src="https://flat.badgen.net/npm/v/nuxt?label=" class="not-prose"></a> | 2025-07-16 | 6 months after 5.x release | [nuxt.com](/docs/4.x)
72
+ **3.x** (maintenance) | <a href="https://www.npmjs.com/package/nuxt?activeTab=versions"><img alt="Nuxt 3.x version" src="https://flat.badgen.net/npm/v/nuxt/3x?label=" class="not-prose"></a> | 2022-11-16 | 2026-01-31 | [nuxt.com](/docs/3.x)
73
73
  **2.x** (unsupported) | <a href="https://www.npmjs.com/package/nuxt?activeTab=versions"><img alt="Nuxt 2.x version" src="https://flat.badgen.net/npm/v/nuxt/2x?label=" class="not-prose"></a> | 2018-09-21 | 2024-06-30 | [v2.nuxt.com](https://v2.nuxt.com/docs)
74
74
  **1.x** (unsupported) | <a href="https://www.npmjs.com/package/nuxt?activeTab=versions"><img alt="Nuxt 1.x version" src="https://flat.badgen.net/npm/v/nuxt/1x?label=" class="not-prose"></a> | 2018-01-08 | 2019-09-21 | &nbsp;
75
75
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nuxt/docs",
3
- "version": "4.0.3",
3
+ "version": "4.1.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/nuxt/nuxt.git",