@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.
- package/1.getting-started/02.installation.md +2 -1
- package/1.getting-started/06.styling.md +1 -1
- package/1.getting-started/09.transitions.md +100 -0
- package/1.getting-started/15.prerendering.md +8 -0
- package/1.getting-started/16.deployment.md +2 -3
- package/1.getting-started/17.testing.md +63 -8
- package/1.getting-started/18.upgrade.md +138 -2
- package/2.directory-structure/1.app/1.layouts.md +60 -1
- package/2.directory-structure/1.server.md +7 -7
- package/3.guide/1.concepts/1.rendering.md +4 -0
- package/3.guide/2.best-practices/hydration.md +13 -13
- package/3.guide/2.best-practices/performance.md +2 -3
- package/3.guide/3.ai/1.mcp.md +1 -1
- package/3.guide/3.ai/2.llms-txt.md +1 -1
- package/3.guide/4.modules/3.recipes-basics.md +90 -0
- package/3.guide/5.recipes/3.custom-usefetch.md +36 -67
- package/3.guide/5.recipes/4.sessions-and-authentication.md +2 -2
- package/3.guide/6.going-further/1.experimental-features.md +81 -5
- package/3.guide/6.going-further/2.hooks.md +1 -1
- package/4.api/1.components/12.nuxt-route-announcer.md +4 -0
- package/4.api/1.components/14.nuxt-announcer.md +81 -0
- package/4.api/1.components/3.nuxt-layout.md +29 -0
- package/4.api/1.components/8.nuxt-island.md +1 -1
- package/4.api/2.composables/create-use-async-data.md +90 -0
- package/4.api/2.composables/create-use-fetch.md +97 -0
- package/4.api/2.composables/use-announcer.md +128 -0
- package/4.api/2.composables/use-async-data.md +2 -2
- package/4.api/2.composables/use-cookie.md +24 -0
- package/4.api/2.composables/use-fetch.md +5 -5
- package/4.api/2.composables/use-lazy-fetch.md +1 -0
- package/4.api/2.composables/use-route-announcer.md +5 -1
- package/4.api/3.utils/clear-nuxt-state.md +4 -2
- package/4.api/3.utils/define-page-meta.md +61 -4
- package/4.api/4.commands/add.md +1 -1
- package/4.api/4.commands/build.md +3 -2
- package/4.api/4.commands/dev.md +2 -1
- package/4.api/4.commands/generate.md +2 -1
- package/4.api/5.kit/11.nitro.md +6 -2
- package/4.api/5.kit/4.autoimports.md +5 -1
- package/4.api/6.advanced/1.hooks.md +1 -1
- package/4.api/6.nuxt-config.md +38 -21
- package/5.community/2.getting-help.md +1 -1
- package/5.community/6.roadmap.md +3 -3
- package/7.migration/11.server.md +1 -1
- 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
|
|
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 >
|
|
124
|
-
2.
|
|
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 `
|
|
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('
|
|
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 {
|
|
410
|
+
const { useStateMock } = vi.hoisted(() => {
|
|
381
411
|
return {
|
|
382
|
-
|
|
412
|
+
useStateMock: vi.fn(() => {
|
|
383
413
|
return { value: 'mocked storage' }
|
|
384
414
|
}),
|
|
385
415
|
}
|
|
386
416
|
})
|
|
387
417
|
|
|
388
|
-
mockNuxtImport('
|
|
389
|
-
return
|
|
418
|
+
mockNuxtImport('useState', () => {
|
|
419
|
+
return useStateMock
|
|
390
420
|
})
|
|
391
421
|
|
|
392
422
|
// Then, inside a test
|
|
393
|
-
|
|
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('
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|