@nuxt/docs 4.3.0 → 4.4.2

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.
Files changed (45) hide show
  1. package/1.getting-started/02.installation.md +2 -1
  2. package/1.getting-started/06.styling.md +1 -1
  3. package/1.getting-started/09.transitions.md +100 -0
  4. package/1.getting-started/15.prerendering.md +8 -0
  5. package/1.getting-started/16.deployment.md +2 -3
  6. package/1.getting-started/17.testing.md +63 -8
  7. package/1.getting-started/18.upgrade.md +138 -2
  8. package/2.directory-structure/1.app/1.layouts.md +60 -1
  9. package/2.directory-structure/1.server.md +7 -7
  10. package/3.guide/1.concepts/1.rendering.md +4 -0
  11. package/3.guide/2.best-practices/hydration.md +13 -13
  12. package/3.guide/2.best-practices/performance.md +2 -3
  13. package/3.guide/3.ai/1.mcp.md +1 -1
  14. package/3.guide/3.ai/2.llms-txt.md +1 -1
  15. package/3.guide/4.modules/3.recipes-basics.md +90 -0
  16. package/3.guide/5.recipes/3.custom-usefetch.md +36 -67
  17. package/3.guide/5.recipes/4.sessions-and-authentication.md +2 -2
  18. package/3.guide/6.going-further/1.experimental-features.md +81 -5
  19. package/3.guide/6.going-further/2.hooks.md +1 -1
  20. package/4.api/1.components/12.nuxt-route-announcer.md +4 -0
  21. package/4.api/1.components/14.nuxt-announcer.md +81 -0
  22. package/4.api/1.components/3.nuxt-layout.md +29 -0
  23. package/4.api/1.components/8.nuxt-island.md +1 -1
  24. package/4.api/2.composables/create-use-async-data.md +90 -0
  25. package/4.api/2.composables/create-use-fetch.md +97 -0
  26. package/4.api/2.composables/use-announcer.md +128 -0
  27. package/4.api/2.composables/use-async-data.md +2 -2
  28. package/4.api/2.composables/use-cookie.md +24 -0
  29. package/4.api/2.composables/use-fetch.md +5 -5
  30. package/4.api/2.composables/use-lazy-fetch.md +1 -0
  31. package/4.api/2.composables/use-route-announcer.md +5 -1
  32. package/4.api/3.utils/clear-nuxt-state.md +4 -2
  33. package/4.api/3.utils/define-page-meta.md +61 -4
  34. package/4.api/4.commands/add.md +1 -1
  35. package/4.api/4.commands/build.md +3 -2
  36. package/4.api/4.commands/dev.md +2 -1
  37. package/4.api/4.commands/generate.md +2 -1
  38. package/4.api/5.kit/11.nitro.md +6 -2
  39. package/4.api/5.kit/4.autoimports.md +5 -1
  40. package/4.api/6.advanced/1.hooks.md +1 -1
  41. package/4.api/6.nuxt-config.md +38 -21
  42. package/5.community/2.getting-help.md +1 -1
  43. package/5.community/6.roadmap.md +3 -3
  44. package/7.migration/11.server.md +1 -1
  45. package/package.json +1 -1
@@ -20,13 +20,14 @@ Or follow the steps below to set up a new Nuxt project on your computer.
20
20
  ### Prerequisites
21
21
 
22
22
  - **Node.js** - [`20.x`](https://nodejs.org/en) or newer (but we recommend the [active LTS release](https://github.com/nodejs/release#release-schedule))
23
- - **Text editor** - There is no IDE requirement, but we recommend [Visual Studio Code](https://code.visualstudio.com/) with the [official Vue extension](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (previously known as Volar) or [WebStorm](https://www.jetbrains.com/webstorm/), which, along with [other JetBrains IDEs](https://www.jetbrains.com/ides/), offers great Nuxt support right out-of-the-box.
23
+ - **Text editor** - There is no IDE requirement, but we recommend [Visual Studio Code](https://code.visualstudio.com/) with the [official Vue extension](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (previously known as Volar) or [WebStorm](https://www.jetbrains.com/webstorm/), which, along with [other JetBrains IDEs](https://www.jetbrains.com/ides/), offers great Nuxt support right out-of-the-box. If you use another editor, such as Neovim, you can configure [Vue Language Server](https://github.com/vuejs/language-tools) support by following the [Vue Language Tools setup guides](https://github.com/vuejs/language-tools/wiki).
24
24
  - **Terminal** - In order to run Nuxt commands
25
25
 
26
26
  ::note
27
27
  ::details
28
28
  :summary[Additional notes for an optimal setup:]
29
29
  - **Node.js**: Make sure to use an even numbered version (20, 22, etc.)
30
+ - **Neovim**: When configuring the Vue TypeScript plugin, make sure `location` points to the `@vue/language-server` package directory, not its binary. See the [Neovim setup guide](https://github.com/vuejs/language-tools/wiki/Neovim) for a working configuration.
30
31
  - **WSL**: If you are using Windows and experience slow HMR, you may want to try using [WSL (Windows Subsystem for Linux)](https://learn.microsoft.com/en-us/windows/wsl/install) which may solve some performance issues.
31
32
  - **Windows slow DNS resolution**: Instead of using `localhost:3000` for local dev server on Windows, use `127.0.0.1` for much faster loading experience on browsers.
32
33
  ::
@@ -155,7 +155,7 @@ Nuxt uses `unhead` under the hood, and you can refer to [its full documentation]
155
155
 
156
156
  If you need more advanced control, you can intercept the rendered html with a hook and modify the head programmatically.
157
157
 
158
- Create a plugin in `~/server/plugins/my-plugin.ts` like this:
158
+ Create a plugin in `~~/server/plugins/my-plugin.ts` like this:
159
159
 
160
160
  <!-- TODO: figure out how to use twoslash to inject types for a different context -->
161
161
 
@@ -459,6 +459,106 @@ definePageMeta({
459
459
  Overriding view transitions on a per-page basis will only have an effect if you have enabled the `experimental.viewTransition` option.
460
460
  ::
461
461
 
462
+ ### View Transition Types
463
+
464
+ [View transition types](https://developer.chrome.com/blog/view-transitions-update-io24#view-transition-types) allow you to apply different CSS animations depending on the type of navigation. This is useful for creating asymmetric transitions (e.g., a different animation when navigating forward vs. backward).
465
+
466
+ Types are set on the [`ViewTransition`](https://developer.mozilla.org/en-US/docs/Web/API/ViewTransition) and can be targeted in CSS using the [`:active-view-transition-type()`](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/:active-view-transition-type) pseudo-class selector.
467
+
468
+ You can set default types globally in your `nuxt.config.ts`:
469
+
470
+ ```ts twoslash [nuxt.config.ts]
471
+ export default defineNuxtConfig({
472
+ app: {
473
+ viewTransition: {
474
+ enabled: true,
475
+ types: ['slide'],
476
+ },
477
+ },
478
+ })
479
+ ```
480
+
481
+ Or configure types per-page using `definePageMeta`. Per-page types support both static arrays and functions for dynamic behavior:
482
+
483
+ ```vue twoslash [pages/detail.vue]
484
+ <script setup lang="ts">
485
+ definePageMeta({
486
+ viewTransition: {
487
+ enabled: true,
488
+ // Types applied to any transition involving this page
489
+ types: ['slide'],
490
+ // Types applied only when navigating TO this page
491
+ toTypes: ['slide-in'],
492
+ // Types applied only when navigating FROM this page
493
+ fromTypes: ['slide-out'],
494
+ },
495
+ })
496
+ </script>
497
+ ```
498
+
499
+ You can also use functions for `types`, `toTypes`, and `fromTypes` in `definePageMeta` to determine types dynamically based on the route:
500
+
501
+ ```vue twoslash [pages/[id].vue]
502
+ <script setup lang="ts">
503
+ definePageMeta({
504
+ viewTransition: {
505
+ enabled: true,
506
+ toTypes: (to, from) => {
507
+ // Slide left when going to a higher ID, right otherwise
508
+ return Number(to.params.id) > Number(from.params.id)
509
+ ? ['slide-left']
510
+ : ['slide-right']
511
+ },
512
+ },
513
+ })
514
+ </script>
515
+ ```
516
+
517
+ Then target these types in your CSS:
518
+
519
+ ```css
520
+ /* Default crossfade */
521
+ ::view-transition-old(root),
522
+ ::view-transition-new(root) {
523
+ animation-duration: 0.3s;
524
+ }
525
+
526
+ /* Slide left animation */
527
+ html:active-view-transition-type(slide-left) {
528
+ &::view-transition-old(root) {
529
+ animation: slide-out-left 0.3s ease-in-out;
530
+ }
531
+ &::view-transition-new(root) {
532
+ animation: slide-in-right 0.3s ease-in-out;
533
+ }
534
+ }
535
+
536
+ /* Slide right animation */
537
+ html:active-view-transition-type(slide-right) {
538
+ &::view-transition-old(root) {
539
+ animation: slide-out-right 0.3s ease-in-out;
540
+ }
541
+ &::view-transition-new(root) {
542
+ animation: slide-in-left 0.3s ease-in-out;
543
+ }
544
+ }
545
+ ```
546
+
547
+ ::note
548
+ Function values for `types`, `toTypes`, and `fromTypes` only work in `definePageMeta`, not in `nuxt.config.ts` (where only static `string[]` is supported).
549
+ ::
550
+
551
+ The `page:view-transition:start` hook provides access to the [`ViewTransition`](https://developer.mozilla.org/en-US/docs/Web/API/ViewTransition) object, which includes a [`types`](https://developer.mozilla.org/en-US/docs/Web/API/ViewTransition/types) property (`ViewTransitionTypeSet`) that can be read or modified at runtime:
552
+
553
+ ```ts [plugins/view-transition.client.ts]
554
+ export default defineNuxtPlugin((nuxtApp) => {
555
+ nuxtApp.hook('page:view-transition:start', (transition) => {
556
+ // Read or modify types at runtime
557
+ console.log([...transition.types])
558
+ })
559
+ })
560
+ ```
561
+
462
562
  If you are also using Vue transitions like `pageTransition` and `layoutTransition` (see above) to achieve the same result as the new View Transitions API, then you may wish to _disable_ Vue transitions if the user's browser supports the newer, native web API. You can do this by creating `~/middleware/disable-vue-transitions.global.ts` with the following contents:
463
563
 
464
564
  ```ts
@@ -49,6 +49,14 @@ Working of the Nitro crawler:
49
49
 
50
50
  This is important to understand since pages that are not linked to a discoverable page can't be pre-rendered automatically.
51
51
 
52
+ ### Payload Extraction
53
+
54
+ Nuxt generates `_payload.json` alongside HTML for:
55
+ - Prerendered routes (at build time)
56
+ - ISR/SWR routes (on first request)
57
+
58
+ Payloads contain serialized data from `useAsyncData` and `useFetch`. Client-side navigation loads these cached payloads instead of re-fetching data. Configure dynamic routes like `pages/[...slug].vue` with route rules: `'/**': { isr: true }`.
59
+
52
60
  ::read-more{to="/docs/4.x/api/commands/generate#nuxt-generate"}
53
61
  Read more about the `nuxt generate` command.
54
62
  ::
@@ -120,9 +120,8 @@ In most cases, Nuxt can work with third-party content that is not generated or c
120
120
 
121
121
  Accordingly, you should make sure that the following options are unchecked / disabled in Cloudflare. Otherwise, unnecessary re-rendering or hydration errors could impact your production application.
122
122
 
123
- 1. Speed > Optimization > Content Optimization > Disable "Rocket Loader™"
124
- 2. Speed > Optimization > Image Optimization > Disable "Mirage"
125
- 3. Scrape Shield > Disable "Email Address Obfuscation"
123
+ 1. Speed > Settings > Content Optimization > Disable "Rocket Loader™"
124
+ 2. Security > Settings > Disable "Email Address Obfuscation"
126
125
 
127
126
  With these settings, you can be sure that Cloudflare won't inject scripts into your Nuxt application that may cause unwanted side effects.
128
127
 
@@ -108,6 +108,7 @@ If you prefer a simpler setup and want all tests to run in the Nuxt environment,
108
108
 
109
109
  ```ts twoslash
110
110
  import { defineVitestConfig } from '@nuxt/test-utils/config'
111
+ import { fileURLToPath } from 'node:url'
111
112
 
112
113
  export default defineVitestConfig({
113
114
  test: {
@@ -299,6 +300,10 @@ it('can also mount an app', async () => {
299
300
  })
300
301
  ```
301
302
 
303
+ The options object accepts `@vue/test-utils` mount options and the following properties:
304
+
305
+ - `route`: the initial route, or `false` to skip the initial route change (default `/`).
306
+
302
307
  #### `renderSuspended`
303
308
 
304
309
  `renderSuspended` allows you to render any Vue component within the Nuxt environment using `@testing-library/vue`, allowing async setup and access to injections from your Nuxt plugins.
@@ -351,14 +356,18 @@ it('can also render an app', async () => {
351
356
  })
352
357
  ```
353
358
 
359
+ The options object accepts `@testing-library/vue` render options and the following properties:
360
+
361
+ - `route`: the initial route, or `false` to skip the initial route change (default `/`).
362
+
354
363
  #### `mockNuxtImport`
355
364
 
356
- `mockNuxtImport` allows you to mock Nuxt's auto import functionality. For example, to mock `useStorage`, you can do so like this:
365
+ `mockNuxtImport` allows you to mock Nuxt's auto import functionality. For example, to mock `useState`, you can do so like this:
357
366
 
358
367
  ```ts twoslash
359
368
  import { mockNuxtImport } from '@nuxt/test-utils/runtime'
360
369
 
361
- mockNuxtImport('useStorage', () => {
370
+ mockNuxtImport('useState', () => {
362
371
  return () => {
363
372
  return { value: 'mocked storage' }
364
373
  }
@@ -367,6 +376,27 @@ mockNuxtImport('useStorage', () => {
367
376
  // your tests here
368
377
  ```
369
378
 
379
+ You can explicitly type the mock for type safety, and use the original implementation passed to the factory function when mocking complex functionality.
380
+
381
+ ```ts twoslash [test/nuxt/import.test.ts]
382
+ import { mockNuxtImport } from '@nuxt/test-utils/runtime'
383
+
384
+ mockNuxtImport<typeof useState>('useState', (original) => {
385
+ return (...args) => {
386
+ return { ...original('some-key'), value: 'mocked state' }
387
+ }
388
+ })
389
+
390
+ // or specify the target to mock
391
+ mockNuxtImport(useState, (original) => {
392
+ return (...args) => {
393
+ return { ...original('some-key'), value: 'mocked state' }
394
+ }
395
+ })
396
+
397
+ // your tests here
398
+ ```
399
+
370
400
  ::note
371
401
  `mockNuxtImport` can only be used once per mocked import per test file. It is actually a macro that gets transformed to `vi.mock` and `vi.mock` is hoisted, as described [in the Vitest docs](https://vitest.dev/api/vi#vi-mock).
372
402
  ::
@@ -377,24 +407,43 @@ If you need to mock a Nuxt import and provide different implementations between
377
407
  import { vi } from 'vitest'
378
408
  import { mockNuxtImport } from '@nuxt/test-utils/runtime'
379
409
 
380
- const { useStorageMock } = vi.hoisted(() => {
410
+ const { useStateMock } = vi.hoisted(() => {
381
411
  return {
382
- useStorageMock: vi.fn(() => {
412
+ useStateMock: vi.fn(() => {
383
413
  return { value: 'mocked storage' }
384
414
  }),
385
415
  }
386
416
  })
387
417
 
388
- mockNuxtImport('useStorage', () => {
389
- return useStorageMock
418
+ mockNuxtImport('useState', () => {
419
+ return useStateMock
390
420
  })
391
421
 
392
422
  // Then, inside a test
393
- useStorageMock.mockImplementation(() => {
423
+ useStateMock.mockImplementation(() => {
394
424
  return { value: 'something else' }
395
425
  })
396
426
  ```
397
427
 
428
+ If you need to mock behavior only inside a test, you can also use the following approach.
429
+
430
+ ```ts twoslash
431
+ import { beforeEach, vi } from 'vitest'
432
+ import { mockNuxtImport } from '@nuxt/test-utils/runtime'
433
+
434
+ mockNuxtImport(useRoute, original => vi.fn(original))
435
+
436
+ beforeEach(() => {
437
+ vi.resetAllMocks()
438
+ })
439
+
440
+ // Then, inside a test
441
+ const useRouteOriginal = vi.mocked(useRoute).getMockImplementation()!
442
+ vi.mocked(useRoute).mockImplementation(
443
+ (...args) => ({ ...useRouteOriginal(...args), path: '/mocked' }),
444
+ )
445
+ ```
446
+
398
447
  #### `mockComponent`
399
448
 
400
449
  `mockComponent` allows you to mock Nuxt's component.
@@ -476,6 +525,12 @@ registerEndpoint('/test/', {
476
525
  })
477
526
  ```
478
527
 
528
+ This object accepts the following properties:
529
+
530
+ - `handler`: the event handler function
531
+ - `method`: (optional) HTTP method to match (e.g., 'GET', 'POST')
532
+ - `once`: (optional) if true, the handler will only be used for the first matching request and then automatically removed
533
+
479
534
  > **Note**: If your requests in a component go to an external API, you can use `baseURL` and then make it empty using [Nuxt Environment Override Config](/docs/4.x/getting-started/configuration#environment-overrides) (`$test`) so all your requests will go to Nitro server.
480
535
 
481
536
  #### Conflict with End-To-End Testing
@@ -489,7 +544,7 @@ If you would like to use both the end-to-end and unit testing functionality of `
489
544
  ```ts twoslash
490
545
  import { mockNuxtImport } from '@nuxt/test-utils/runtime'
491
546
 
492
- mockNuxtImport('useStorage', () => {
547
+ mockNuxtImport('useState', () => {
493
548
  return () => {
494
549
  return { value: 'mocked storage' }
495
550
  }
@@ -59,6 +59,9 @@ export default defineNuxtConfig({
59
59
  When you set your `future.compatibilityVersion` to `5`, defaults throughout your Nuxt configuration will change to opt in to Nuxt v5 behavior, including:
60
60
 
61
61
  - **Vite Environment API**: Automatically enables the new [Vite Environment API](/docs/4.x/getting-started/upgrade#migration-to-vite-environment-api) for improved build configuration
62
+ - **Normalized Page Names**: Page component names will [match their route names](/docs/4.x/getting-started/upgrade#normalized-page-component-names) for consistent `<KeepAlive>` behavior
63
+ - **`clearNuxtState` resets to defaults**: `clearNuxtState` will [reset state to its initial value](/docs/4.x/getting-started/upgrade#respect-defaults-when-clearing-usestate) instead of setting it to `undefined`
64
+ - **Non-async `callHook`**: [`callHook` may return `void`](/docs/4.x/getting-started/upgrade#non-async-callhook) instead of always returning a `Promise`
62
65
  - Other Nuxt 5 improvements and changes as they become available
63
66
 
64
67
  ::note
@@ -178,6 +181,52 @@ addVitePlugin(() => ({
178
181
  Learn more about Vite's Environment API
179
182
  ::
180
183
 
184
+ ### Non-Async `callHook`
185
+
186
+ 🚦 **Impact Level**: Minimal
187
+
188
+ #### What Changed
189
+
190
+ With the upgrade to [hookable v6](https://github.com/unjs/hookable), `callHook` may now return `void` instead of always returning `Promise<void>`. This is a significant performance improvement that avoids unnecessary `Promise` allocations when there are no registered hooks or all hooks are synchronous.
191
+
192
+ By default (with `compatibilityVersion: 4`), Nuxt wraps `callHook` with `Promise.resolve()` so that existing `.then()` and `.catch()` chaining continues to work. With `compatibilityVersion: 5`, this wrapper is removed.
193
+
194
+ ::tip
195
+ This affects both build-time Nuxt hooks (used by Nuxt modules) and runtime Nuxt hooks (which you might use in your application code).
196
+ ::
197
+
198
+ #### Reasons for Change
199
+
200
+ Hookable v6's `callHook` is 20-40x faster because it avoids creating a `Promise` when one is not needed. This benefits applications with many hook call sites.
201
+
202
+ #### Migration Steps
203
+
204
+ If you or your modules use `callHook` with `.then()` or `.catch()` chaining, switch to `await`:
205
+
206
+ ```diff
207
+ - nuxtApp.callHook('my:hook', data).then(() => { ... })
208
+ + await nuxtApp.callHook('my:hook', data)
209
+ ```
210
+
211
+ ```diff
212
+ - nuxtApp.hooks.callHook('my:hook', data).catch(err => { ... })
213
+ + try { await nuxtApp.hooks.callHook('my:hook', data) } catch (err) { ... }
214
+ ```
215
+
216
+ ::tip
217
+ You can test this feature early by setting `future.compatibilityVersion: 5` (see [Testing Nuxt 5](/docs/4.x/getting-started/upgrade#testing-nuxt-5)) or by enabling it explicitly with `experimental.asyncCallHook: false`.
218
+ ::
219
+
220
+ Alternatively, you can ensure `callHook` always returns a `Promise` with:
221
+
222
+ ```ts twoslash [nuxt.config.ts]
223
+ export default defineNuxtConfig({
224
+ experimental: {
225
+ asyncCallHook: true,
226
+ },
227
+ })
228
+ ```
229
+
181
230
  ## Migrating to Nuxt 4
182
231
 
183
232
  Nuxt 4 includes significant improvements and changes. This guide will help you migrate your existing Nuxt 3 application to Nuxt 4.
@@ -272,6 +321,7 @@ Nuxt now defaults to a new directory structure, with backwards compatibility (so
272
321
  - `layers/`, `modules/` and `public/` are resolved relative to `<rootDir>` by default
273
322
  - if using [Nuxt Content v2.13+](https://github.com/nuxt/content/pull/2649), `content/` is resolved relative to `<rootDir>`
274
323
  - a new `dir.app` is added, which is the directory we look for `router.options.ts` and `spa-loading-template.html` - this defaults to `<srcDir>/`
324
+ - a new [`shared/`](/docs/4.x/directory-structure/shared) directory is available for code shared between the Vue app and the Nitro server, with auto-imports for `shared/utils/` and `shared/types/`
275
325
 
276
326
  <details>
277
327
 
@@ -298,6 +348,8 @@ modules/
298
348
  node_modules/
299
349
  public/
300
350
  shared/
351
+ types/
352
+ utils/
301
353
  server/
302
354
  api/
303
355
  middleware/
@@ -326,14 +378,14 @@ With this new structure, the `~` alias now points to the `app/` directory by def
326
378
 
327
379
  1. Create a new directory called `app/`.
328
380
  1. Move your `assets/`, `components/`, `composables/`, `app/layouts/`, `app/middleware/`, `app/pages/`, `app/plugins/` and `utils/` folders under it, as well as `app.vue`, `error.vue`, `app.config.ts`. If you have an `app/router-options.ts` or `app/spa-loading-template.html`, these paths remain the same.
329
- 1. Make sure your `nuxt.config.ts`, `content/`, `layers/`, `modules/`, `public/` and `server/` folders remain outside the `app/` folder, in the root of your project.
381
+ 1. Make sure your `nuxt.config.ts`, `content/`, `layers/`, `modules/`, `public/`, `shared/` and `server/` folders remain outside the `app/` folder, in the root of your project.
330
382
  1. Remember to update any third-party configuration files to work with the new directory structure, such as your `tailwindcss` or `eslint` configuration (if required - `@nuxtjs/tailwindcss` should automatically configure `tailwindcss` correctly).
331
383
 
332
384
  ::tip
333
385
  You can automate this migration by running `npx codemod@latest nuxt/4/file-structure`
334
386
  ::
335
387
 
336
- However, migration is _not required_. If you wish to keep your current folder structure, Nuxt should auto-detect it. (If it does not, please raise an issue.) The one exception is that if you _already_ have a custom `srcDir`. In this case, you should be aware that your `modules/`, `public/` and `server/` folders will be resolved from your `rootDir` rather than from your custom `srcDir`. You can override this by configuring `dir.modules`, `dir.public` and `serverDir` if you need to.
388
+ However, migration is _not required_. If you wish to keep your current folder structure, Nuxt should auto-detect it. (If it does not, please raise an issue.) The one exception is that if you _already_ have a custom `srcDir`. In this case, you should be aware that your `modules/`, `public/`, `shared/` and `server/` folders will be resolved from your `rootDir` rather than from your custom `srcDir`. You can override this by configuring `dir.modules`, `dir.public` and `serverDir` if you need to.
337
389
 
338
390
  You can also force a v3 folder structure with the following configuration:
339
391
 
@@ -846,6 +898,55 @@ If you provide a custom `default` value for `useAsyncData`, this will now be use
846
898
 
847
899
  Often users set an appropriately empty value, such as an empty array, to avoid the need to check for `null`/`undefined` when iterating over it. This should be respected when resetting/clearing the data.
848
900
 
901
+ ### Respect defaults when clearing `useState`
902
+
903
+ 🚦 **Impact Level**: Minimal
904
+
905
+ #### What Changed
906
+
907
+ With `compatibilityVersion: 5`, `clearNuxtState` will reset state to its initial value (provided by the `init` function of `useState`) instead of setting it to `undefined`. This aligns `clearNuxtState` behavior with `clearNuxtData`, which already resets to defaults.
908
+
909
+ #### Reasons for Change
910
+
911
+ When `clearNuxtState` sets state to `undefined`, composables that depend on that state can crash because they expect the state to always have a valid shape (e.g., accessing properties on `undefined`). Resetting to the `init` value ensures state always has a usable default.
912
+
913
+ #### Migration Steps
914
+
915
+ If you rely on `clearNuxtState` setting state to `undefined`, you can explicitly pass `{ reset: false }`:
916
+
917
+ ```diff
918
+ - clearNuxtState('myKey')
919
+ + clearNuxtState('myKey', { reset: false })
920
+ ```
921
+
922
+ Alternatively, you can revert to the previous behavior with:
923
+
924
+ ```ts twoslash [nuxt.config.ts]
925
+ export default defineNuxtConfig({
926
+ experimental: {
927
+ defaults: {
928
+ useState: {
929
+ resetOnClear: false,
930
+ },
931
+ },
932
+ },
933
+ })
934
+ ```
935
+
936
+ You can also opt in to this behavior today without setting `compatibilityVersion: 5`:
937
+
938
+ ```ts twoslash [nuxt.config.ts]
939
+ export default defineNuxtConfig({
940
+ experimental: {
941
+ defaults: {
942
+ useState: {
943
+ resetOnClear: true,
944
+ },
945
+ },
946
+ },
947
+ })
948
+ ```
949
+
849
950
  ### Alignment of `pending` value in `useAsyncData` and `useFetch`
850
951
 
851
952
  🚦 **Impact Level**: Medium
@@ -1321,6 +1422,41 @@ export default defineNuxtConfig({
1321
1422
  Read more about Nitro's prerender configuration options.
1322
1423
  ::
1323
1424
 
1425
+ ### Normalized Page Component Names
1426
+
1427
+ 🚦 **Impact Level**: Minimal
1428
+
1429
+ #### What Changed
1430
+
1431
+ When `future.compatibilityVersion` is set to `5` (or `experimental.normalizePageNames` is enabled), page component names match their route names instead of using the filename. For example, `pages/foo/index.vue` will have the component name `foo` instead of `index`.
1432
+
1433
+ #### Reasons for Change
1434
+
1435
+ Previously, Vue assigned component names based on the filename. This meant multiple pages like `pages/foo/index.vue` and `pages/bar/index.vue` would both have the component name `index`. This made `<KeepAlive>` with `include`/`exclude` filters unreliable and required manually adding `defineOptions({ name: '...' })` to each page.
1436
+
1437
+ #### Migration Steps
1438
+
1439
+ If you rely on the current component names (e.g. in `<KeepAlive>` `include`/`exclude` lists), update them to use route names instead of filenames.
1440
+
1441
+ ```diff
1442
+ <template>
1443
+ <NuxtPage :keepalive="{
1444
+ - include: ['index']
1445
+ + include: ['foo']
1446
+ }" />
1447
+ </template>
1448
+ ```
1449
+
1450
+ To disable this behavior:
1451
+
1452
+ ```ts twoslash [nuxt.config.ts]
1453
+ export default defineNuxtConfig({
1454
+ experimental: {
1455
+ normalizePageNames: false,
1456
+ },
1457
+ })
1458
+ ```
1459
+
1324
1460
  ## Nuxt 2 vs. Nuxt 3+
1325
1461
 
1326
1462
  In the table below, there is a quick comparison between 3 versions of Nuxt:
@@ -122,7 +122,7 @@ File | Layout Name
122
122
 
123
123
  You can also use the [`setPageLayout`](/docs/4.x/api/utils/set-page-layout) helper to change the layout dynamically:
124
124
 
125
- ```vue twoslash
125
+ ```vue twoslash [app/pages/index.vue]
126
126
  <script setup lang="ts">
127
127
  declare module 'nuxt/app' {
128
128
  interface NuxtLayouts {
@@ -168,6 +168,65 @@ This is useful when you want to manage layouts centrally in your configuration r
168
168
 
169
169
  :link-example{to="/docs/4.x/examples/features/layouts"}
170
170
 
171
+ ## Passing Props to Layouts
172
+
173
+ You can pass props to layouts in several ways.
174
+
175
+ ### Via `definePageMeta`
176
+
177
+ Use the object syntax for the `layout` property to pass props directly from your page:
178
+
179
+ ::code-group
180
+
181
+ ```vue [app/pages/dashboard.vue]
182
+ <script setup lang="ts">
183
+ definePageMeta({
184
+ layout: {
185
+ name: 'panel',
186
+ props: {
187
+ sidebar: true,
188
+ title: 'Dashboard',
189
+ },
190
+ },
191
+ })
192
+ </script>
193
+ ```
194
+
195
+ ```vue [app/layouts/panel.vue]
196
+ <script setup lang="ts">
197
+ const props = defineProps<{
198
+ sidebar?: boolean
199
+ title?: string
200
+ }>()
201
+ </script>
202
+
203
+ <template>
204
+ <div>
205
+ <aside v-if="sidebar">
206
+ Sidebar
207
+ </aside>
208
+ <main>
209
+ <h1>{{ title }}</h1>
210
+ <slot />
211
+ </main>
212
+ </div>
213
+ </template>
214
+ ```
215
+
216
+ ::
217
+
218
+ ::tip
219
+ Props are fully typed based on your layout's `defineProps`. You'll get autocomplete and type-checking in your editor.
220
+ ::
221
+
222
+ ### Via `setPageLayout`
223
+
224
+ You can also pass props when changing the layout dynamically with [`setPageLayout`](/docs/4.x/api/utils/set-page-layout):
225
+
226
+ ```ts
227
+ setPageLayout('panel', { sidebar: true, title: 'Dashboard' })
228
+ ```
229
+
171
230
  ## Overriding a Layout on a Per-page Basis
172
231
 
173
232
  If you are using pages, you can take full control by setting `layout: false` and then using the `<NuxtLayout>` component within the page.
@@ -43,11 +43,11 @@ const { data } = await useFetch('/api/hello')
43
43
 
44
44
  ## Server Routes
45
45
 
46
- Files inside the `~/server/api` are automatically prefixed with `/api` in their route.
46
+ Files inside the `~~/server/api` are automatically prefixed with `/api` in their route.
47
47
 
48
48
  :video-accordion{title="Watch a video from Vue School on API routes" videoId="761468863" platform="vimeo"}
49
49
 
50
- To add server routes without `/api` prefix, put them into `~/server/routes` directory.
50
+ To add server routes without `/api` prefix, put them into `~~/server/routes` directory.
51
51
 
52
52
  **Example:**
53
53
 
@@ -63,7 +63,7 @@ Note that currently server routes do not support the full functionality of dynam
63
63
 
64
64
  ## Server Middleware
65
65
 
66
- Nuxt will automatically read in any file in the `~/server/middleware` to create server middleware for your project.
66
+ Nuxt will automatically read in any file in the `~~/server/middleware` to create server middleware for your project.
67
67
 
68
68
  Middleware handlers will run on every request before any other server route to add or check headers, log requests, or extend the event's request object.
69
69
 
@@ -87,7 +87,7 @@ export default defineEventHandler((event) => {
87
87
 
88
88
  ## Server Plugins
89
89
 
90
- Nuxt will automatically read any files in the `~/server/plugins` directory and register them as Nitro plugins. This allows extending Nitro's runtime behavior and hooking into lifecycle events.
90
+ Nuxt will automatically read any files in the `~~/server/plugins` directory and register them as Nitro plugins. This allows extending Nitro's runtime behavior and hooking into lifecycle events.
91
91
 
92
92
  **Example:**
93
93
 
@@ -105,7 +105,7 @@ Server routes are powered by [h3js/h3](https://github.com/h3js/h3) which comes w
105
105
 
106
106
  :read-more{to="https://www.jsdocs.io/package/h3#package-index-functions" title="Available H3 Request Helpers" target="_blank"}
107
107
 
108
- You can add more helpers yourself inside the `~/server/utils` directory.
108
+ You can add more helpers yourself inside the `~~/server/utils` directory.
109
109
 
110
110
  For example, you can define a custom handler utility that wraps the original handler and performs additional operations before returning the final response.
111
111
 
@@ -218,7 +218,7 @@ export default defineEventHandler((event) => {
218
218
 
219
219
  Catch-all routes are helpful for fallback route handling.
220
220
 
221
- For example, creating a file named `~/server/api/foo/[...].ts` will register a catch-all route for all requests that do not match any route handler, such as `/api/foo/bar/baz`.
221
+ For example, creating a file named `~~/server/api/foo/[...].ts` will register a catch-all route for all requests that do not match any route handler, such as `/api/foo/bar/baz`.
222
222
 
223
223
  ```ts [server/api/foo/[...\\].ts]
224
224
  export default defineEventHandler((event) => {
@@ -228,7 +228,7 @@ export default defineEventHandler((event) => {
228
228
  })
229
229
  ```
230
230
 
231
- You can set a name for the catch-all route by using `~/server/api/foo/[...slug].ts` and access it via `event.context.params.slug`.
231
+ You can set a name for the catch-all route by using `~~/server/api/foo/[...slug].ts` and access it via `event.context.params.slug`.
232
232
 
233
233
  ```ts [server/api/foo/[...slug\\].ts]
234
234
  export default defineEventHandler((event) => {
@@ -193,6 +193,10 @@ The different properties you can use are the following:
193
193
  - `noScripts: boolean`{lang=ts} - Disables rendering of Nuxt scripts and JS resource hints for sections of your site.
194
194
  - `appMiddleware: string | string[] | Record<string, boolean>`{lang=ts} - Allows you to define middleware that should or should not run for page paths within the Vue app part of your application (that is, not your Nitro routes)
195
195
 
196
+ ::note
197
+ Routes using `isr` or `swr` also generate `_payload.json` files alongside HTML. Client-side navigation loads these cached payloads instead of re-fetching data. Configure dynamic routes like `pages/[...slug].vue` with glob patterns: `'/**': { isr: true }`.
198
+ ::
199
+
196
200
  Whenever possible, route rules will be automatically applied to the deployment platform's native rules for optimal performances (Netlify and Vercel are currently supported).
197
201
 
198
202
  ::important