@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
|
@@ -1,37 +1,37 @@
|
|
|
1
1
|
---
|
|
2
|
-
navigation.title: 'Nuxt and
|
|
3
|
-
title: Nuxt and
|
|
2
|
+
navigation.title: 'Nuxt and Hydration'
|
|
3
|
+
title: Nuxt and Hydration
|
|
4
4
|
description: Why fixing hydration issues is important
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
When developing, you may face hydration issues. Don't ignore those warnings.
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
## Why is it important to fix them?
|
|
10
10
|
|
|
11
11
|
Hydration mismatches are not just warnings - they are indicators of serious problems that can break your application:
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
### Performance Impact
|
|
14
14
|
|
|
15
15
|
- **Increased time to interactive**: Hydration errors force Vue to re-render the entire component tree, which will increase the time for your Nuxt app to become interactive
|
|
16
16
|
- **Poor user experience**: Users may see content flashing or unexpected layout shifts
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
### Functionality Issues
|
|
19
19
|
|
|
20
20
|
- **Broken interactivity**: Event listeners may not attach properly, leaving buttons and forms non-functional
|
|
21
21
|
- **State inconsistencies**: Application state can become out of sync between what the user sees and what the application thinks is rendered
|
|
22
22
|
- **SEO problems**: Search engines may index different content than what users actually see
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
## How to detect them
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
### Development Console Warnings
|
|
27
27
|
|
|
28
28
|
Vue will log hydration mismatch warnings in the browser console during development:
|
|
29
29
|
|
|
30
30
|

|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
## Common reasons
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
### Browser-only APIs in Server Context
|
|
35
35
|
|
|
36
36
|
**Problem**: Using browser-specific APIs during server-side rendering.
|
|
37
37
|
|
|
@@ -60,7 +60,7 @@ const userTheme = useCookie('theme', { default: () => 'light' })
|
|
|
60
60
|
</script>
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
|
|
63
|
+
### Inconsistent Data
|
|
64
64
|
|
|
65
65
|
**Problem**: Different data between server and client.
|
|
66
66
|
|
|
@@ -82,7 +82,7 @@ const state = useState('random', () => Math.random())
|
|
|
82
82
|
</script>
|
|
83
83
|
```
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
### Conditional Rendering Based on Client State
|
|
86
86
|
|
|
87
87
|
**Problem**: Using client-only conditions during SSR.
|
|
88
88
|
|
|
@@ -105,7 +105,7 @@ const state = useState('random', () => Math.random())
|
|
|
105
105
|
</template>
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
### Third-party Libraries with Side Effects
|
|
109
109
|
|
|
110
110
|
**Problem**: Libraries that modify the DOM or have browser dependencies (this happens a LOT with tag managers).
|
|
111
111
|
|
|
@@ -129,7 +129,7 @@ onMounted(async () => {
|
|
|
129
129
|
</script>
|
|
130
130
|
```
|
|
131
131
|
|
|
132
|
-
|
|
132
|
+
### Dynamic Content Based on Time
|
|
133
133
|
|
|
134
134
|
**Problem**: Content that changes based on current time.
|
|
135
135
|
|
|
@@ -142,9 +142,8 @@ Images in your website can usually be separated by importance; the ones that are
|
|
|
142
142
|
<NuxtImg
|
|
143
143
|
src="/hero-banner.jpg"
|
|
144
144
|
format="webp"
|
|
145
|
-
preload
|
|
145
|
+
:preload="{ fetchPriority: 'high' }"
|
|
146
146
|
loading="eager"
|
|
147
|
-
fetch-priority="high"
|
|
148
147
|
width="200"
|
|
149
148
|
height="100"
|
|
150
149
|
/>
|
|
@@ -154,7 +153,7 @@ Images in your website can usually be separated by importance; the ones that are
|
|
|
154
153
|
src="/facebook-logo.jpg"
|
|
155
154
|
format="webp"
|
|
156
155
|
loading="lazy"
|
|
157
|
-
|
|
156
|
+
fetchpriority="low"
|
|
158
157
|
width="200"
|
|
159
158
|
height="100"
|
|
160
159
|
/>
|
package/3.guide/3.ai/1.mcp.md
CHANGED
|
@@ -79,7 +79,7 @@ The Nuxt connector will appear in the composer's "Developer mode" tool later dur
|
|
|
79
79
|
### Claude Code
|
|
80
80
|
|
|
81
81
|
::note{icon="i-lucide-info"}
|
|
82
|
-
**Ensure Claude Code is installed** - Visit [Anthropic's documentation](https://
|
|
82
|
+
**Ensure Claude Code is installed** - Visit [Anthropic's documentation](https://code.claude.com/docs/en/quickstart) for installation instructions.
|
|
83
83
|
::
|
|
84
84
|
|
|
85
85
|
Add the server using the CLI command:
|
|
@@ -42,7 +42,7 @@ Nuxt provides specialized LLMs.txt files that you can reference in Cursor for be
|
|
|
42
42
|
1. **Direct reference**: Mention the LLMs.txt URLs when asking questions
|
|
43
43
|
2. Add these specific URLs to your project context using `@docs`
|
|
44
44
|
|
|
45
|
-
[Read more about Cursor Web and Docs Search](https://cursor.com/docs/context/
|
|
45
|
+
[Read more about Cursor Web and Docs Search](https://cursor.com/docs/context/mentions)
|
|
46
46
|
|
|
47
47
|
### Windsurf
|
|
48
48
|
|
|
@@ -200,6 +200,96 @@ It is highly recommended to prefix your exports to avoid conflicts with user cod
|
|
|
200
200
|
Note that all components, pages, composables and other files that would be normally placed in your `app/` folder need to be in `runtime/app/`. This will mean they can be type checked properly.
|
|
201
201
|
::
|
|
202
202
|
|
|
203
|
+
### Add Keyed Functions
|
|
204
|
+
|
|
205
|
+
Sometimes, you may need to maintain state consistency between the server and the client. Examples include Nuxt's built-in `useState` or `useAsyncData` composables. Nuxt provides a way to register such functions for automatic key injection.
|
|
206
|
+
|
|
207
|
+
When a function is registered, Nuxt’s compiler automatically injects a unique key as an additional argument if the function is called with fewer than the specified number of arguments. This key remains stable between server-side rendering and client hydration.
|
|
208
|
+
|
|
209
|
+
::tip
|
|
210
|
+
The injected key is a hash derived from the file path and call location.
|
|
211
|
+
::
|
|
212
|
+
|
|
213
|
+
Use the `keyedComposables` option to register your function:
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
import { createResolver, defineNuxtModule } from '@nuxt/kit'
|
|
217
|
+
|
|
218
|
+
export default defineNuxtModule({
|
|
219
|
+
setup (options, nuxt) {
|
|
220
|
+
const resolver = createResolver(import.meta.url)
|
|
221
|
+
|
|
222
|
+
nuxt.options.optimization.keyedComposables.push({
|
|
223
|
+
name: 'useMyState',
|
|
224
|
+
source: resolver.resolve('./runtime/composables/state'),
|
|
225
|
+
argumentLength: 2,
|
|
226
|
+
})
|
|
227
|
+
},
|
|
228
|
+
})
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
The `keyedComposables` configuration accepts an array of objects with the following properties:
|
|
232
|
+
|
|
233
|
+
| Property | Type | Description |
|
|
234
|
+
|------------------|----------|----------------------------------------------------------------------------------------------------------------------------|
|
|
235
|
+
| `name` | `string` | The function name. Use `'default'` for default exports (the callable name will be derived from the filename in camelCase). |
|
|
236
|
+
| `source` | `string` | Resolved path to the file where the function is defined. Supports Nuxt aliases (`~`, `@`, etc.) |
|
|
237
|
+
| `argumentLength` | `number` | Maximum number of arguments the function accepts. When called with fewer arguments, a unique key is injected. |
|
|
238
|
+
|
|
239
|
+
For example, with `argumentLength: 2`:
|
|
240
|
+
|
|
241
|
+
```ts
|
|
242
|
+
useMyState() // useMyState('$HJiaryoL2y')
|
|
243
|
+
useMyState('myKey') // useMyState('myKey', '$HJiaryoL2y')
|
|
244
|
+
useMyState('a', 'b') // not transformed (already has 2 arguments)
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
::warning
|
|
248
|
+
The key injection plugin verifies the exact resolved import source of each function call. It does not follow barrel exports. The function must be exported from the exact source file specified in the `source` property.
|
|
249
|
+
|
|
250
|
+
```ts
|
|
251
|
+
// ✅ Works - direct import matches the configured source
|
|
252
|
+
import { useMyState } from 'my-module/runtime/composables/state'
|
|
253
|
+
|
|
254
|
+
// ❌ Won't work - re-exported through a barrel file
|
|
255
|
+
import { useMyState } from 'my-module/runtime/composables' // index.ts barrel
|
|
256
|
+
```
|
|
257
|
+
::
|
|
258
|
+
|
|
259
|
+
::warning
|
|
260
|
+
The function call must be statically analyzable. The compiler cannot inject keys for dynamic or indirect function calls.
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
import { useMyState } from 'my-module/runtime/composables/state'
|
|
264
|
+
import * as composables from 'my-module/runtime/composables/state'
|
|
265
|
+
|
|
266
|
+
// ✅ Works - direct function call
|
|
267
|
+
useMyState()
|
|
268
|
+
|
|
269
|
+
// ✅ Works - called on namespace import
|
|
270
|
+
composables.useMyState()
|
|
271
|
+
|
|
272
|
+
// ❌ Won't work - dynamic property access
|
|
273
|
+
const name = 'useMyState'
|
|
274
|
+
composables[name]()
|
|
275
|
+
|
|
276
|
+
// ❌ Won't work - reassigned to a variable
|
|
277
|
+
const myFn = useMyState
|
|
278
|
+
myFn()
|
|
279
|
+
|
|
280
|
+
// ❌ Won't work - passed as a callback
|
|
281
|
+
someFunction(useMyState)
|
|
282
|
+
|
|
283
|
+
// ❌ Won't work - destructured with renaming in a nested scope
|
|
284
|
+
function setup () {
|
|
285
|
+
const { useMyState: localState } = composables
|
|
286
|
+
localState() // not transformed
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// ...
|
|
290
|
+
```
|
|
291
|
+
::
|
|
292
|
+
|
|
203
293
|
## Add Server Routes
|
|
204
294
|
|
|
205
295
|
```ts
|
|
@@ -10,19 +10,46 @@ The [`$fetch`](/docs/4.x/api/utils/dollarfetch) utility function (used by the [`
|
|
|
10
10
|
|
|
11
11
|
However, Nuxt provides a way to create a custom fetcher for your API (or multiple fetchers if you have multiple APIs to call).
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Recipe: API Client with Auth
|
|
14
14
|
|
|
15
|
-
Let's
|
|
15
|
+
Let's say you have an external API at `https://api.nuxt.com` that requires JWT authentication via [nuxt-auth-utils](https://github.com/atinux/nuxt-auth-utils), and you want to redirect to `/login` on `401` responses.
|
|
16
|
+
|
|
17
|
+
```ts [app/composables/useAPI.ts]
|
|
18
|
+
export const useAPI = createUseFetch({
|
|
19
|
+
baseURL: 'https://api.nuxt.com',
|
|
20
|
+
onRequest ({ options }) {
|
|
21
|
+
const { session } = useUserSession()
|
|
22
|
+
if (session.value?.token) {
|
|
23
|
+
options.headers.set('Authorization', `Bearer ${session.value.token}`)
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
async onResponseError ({ response }) {
|
|
27
|
+
if (response.status === 401) {
|
|
28
|
+
await navigateTo('/login')
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
})
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Now every call to `useAPI` automatically includes the auth header and handles 401 redirects:
|
|
35
|
+
|
|
36
|
+
```vue [app/pages/dashboard.vue]
|
|
37
|
+
<script setup lang="ts">
|
|
38
|
+
const { data: profile } = await useAPI('/me')
|
|
39
|
+
const { data: orders } = await useAPI('/orders')
|
|
40
|
+
</script>
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
:read-more{to="/docs/4.x/api/composables/create-use-fetch"}
|
|
44
|
+
|
|
45
|
+
## Recipe: Custom `$fetch` Instance
|
|
46
|
+
|
|
47
|
+
If you need lower-level control, you can create a custom `$fetch` instance with a [Nuxt plugin](/docs/4.x/directory-structure/app/plugins) and either use it with `useAsyncData` directly or pass it to `createUseFetch`.
|
|
16
48
|
|
|
17
49
|
::note
|
|
18
50
|
`$fetch` is a configured instance of [ofetch](https://github.com/unjs/ofetch) which supports adding the base URL of your Nuxt server as well as direct function calls during SSR (avoiding HTTP roundtrips).
|
|
19
51
|
::
|
|
20
52
|
|
|
21
|
-
Let's pretend here that:
|
|
22
|
-
- The main API is https://api.nuxt.com
|
|
23
|
-
- We are storing the JWT token in a session with [nuxt-auth-utils](https://github.com/atinux/nuxt-auth-utils)
|
|
24
|
-
- If the API responds with a `401` status code, we redirect the user to the `/login` page
|
|
25
|
-
|
|
26
53
|
```ts [app/plugins/api.ts]
|
|
27
54
|
export default defineNuxtPlugin((nuxtApp) => {
|
|
28
55
|
const { session } = useUserSession()
|
|
@@ -31,7 +58,6 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|
|
31
58
|
baseURL: 'https://api.nuxt.com',
|
|
32
59
|
onRequest ({ request, options, error }) {
|
|
33
60
|
if (session.value?.token) {
|
|
34
|
-
// note that this relies on ofetch >= 1.4.0 - you may need to refresh your lockfile
|
|
35
61
|
options.headers.set('Authorization', `Bearer ${session.value?.token}`)
|
|
36
62
|
}
|
|
37
63
|
},
|
|
@@ -42,7 +68,6 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|
|
42
68
|
},
|
|
43
69
|
})
|
|
44
70
|
|
|
45
|
-
// Expose to useNuxtApp().$api
|
|
46
71
|
return {
|
|
47
72
|
provide: {
|
|
48
73
|
api,
|
|
@@ -51,7 +76,7 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|
|
51
76
|
})
|
|
52
77
|
```
|
|
53
78
|
|
|
54
|
-
|
|
79
|
+
You can then use the custom `$fetch` instance directly:
|
|
55
80
|
|
|
56
81
|
```vue [app/app.vue]
|
|
57
82
|
<script setup>
|
|
@@ -61,65 +86,9 @@ const { data: modules } = await useAsyncData('modules', () => $api('/modules'))
|
|
|
61
86
|
```
|
|
62
87
|
|
|
63
88
|
::callout
|
|
64
|
-
Wrapping with [`useAsyncData`](/docs/4.x/api/composables/use-async-data) **
|
|
65
|
-
::
|
|
66
|
-
|
|
67
|
-
## Custom `useFetch`/`useAsyncData`
|
|
68
|
-
|
|
69
|
-
Now that `$api` has the logic we want, let's create a `useAPI` composable to replace the usage of `useAsyncData` + `$api`:
|
|
70
|
-
|
|
71
|
-
```ts [app/composables/useAPI.ts]
|
|
72
|
-
import type { UseFetchOptions } from 'nuxt/app'
|
|
73
|
-
|
|
74
|
-
export function useAPI<T> (
|
|
75
|
-
url: string | (() => string),
|
|
76
|
-
options?: UseFetchOptions<T>,
|
|
77
|
-
) {
|
|
78
|
-
return useFetch(url, {
|
|
79
|
-
...options,
|
|
80
|
-
$fetch: useNuxtApp().$api as typeof $fetch,
|
|
81
|
-
})
|
|
82
|
-
}
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
Let's use the new composable and have a nice and clean component:
|
|
86
|
-
|
|
87
|
-
```vue [app/app.vue]
|
|
88
|
-
<script setup>
|
|
89
|
-
const { data: modules } = await useAPI('/modules')
|
|
90
|
-
</script>
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
If you want to customize the type of any error returned, you can also do so:
|
|
94
|
-
|
|
95
|
-
```ts
|
|
96
|
-
import type { FetchError } from 'ofetch'
|
|
97
|
-
import type { UseFetchOptions } from 'nuxt/app'
|
|
98
|
-
|
|
99
|
-
interface CustomError {
|
|
100
|
-
message: string
|
|
101
|
-
status: number
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export function useAPI<T> (
|
|
105
|
-
url: string | (() => string),
|
|
106
|
-
options?: UseFetchOptions<T>,
|
|
107
|
-
) {
|
|
108
|
-
return useFetch<T, FetchError<CustomError>>(url, {
|
|
109
|
-
...options,
|
|
110
|
-
$fetch: useNuxtApp().$api,
|
|
111
|
-
})
|
|
112
|
-
}
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
::note
|
|
116
|
-
This example demonstrates how to use a custom `useFetch`, but the same structure is identical for a custom `useAsyncData`.
|
|
89
|
+
Wrapping with [`useAsyncData`](/docs/4.x/api/composables/use-async-data) **avoids double data fetching when doing server-side rendering** (server & client on hydration).
|
|
117
90
|
::
|
|
118
91
|
|
|
119
92
|
:link-example{to="/docs/4.x/examples/advanced/use-custom-fetch-composable"}
|
|
120
93
|
|
|
121
94
|
:video-accordion{title="Watch a video about custom $fetch and Repository Pattern in Nuxt" videoId="jXH8Tr-exhI"}
|
|
122
|
-
|
|
123
|
-
::note
|
|
124
|
-
We are currently discussing to find a cleaner way to let you create a custom fetcher, see https://github.com/nuxt/nuxt/issues/14736.
|
|
125
|
-
::
|
|
@@ -47,7 +47,7 @@ Let's create a `/api/login` API route that will accept a POST request with the e
|
|
|
47
47
|
import { z } from 'zod'
|
|
48
48
|
|
|
49
49
|
const bodySchema = z.object({
|
|
50
|
-
email: z.
|
|
50
|
+
email: z.email(),
|
|
51
51
|
password: z.string().min(8),
|
|
52
52
|
})
|
|
53
53
|
|
|
@@ -210,7 +210,7 @@ We've successfully set up a very basic user authentication and session managemen
|
|
|
210
210
|
|
|
211
211
|
As next steps, you can:
|
|
212
212
|
- Add authentication using the [20+ supported OAuth providers](https://github.com/atinux/nuxt-auth-utils?tab=readme-ov-file#supported-oauth-providers)
|
|
213
|
-
- Add a database to store users, see [Nitro SQL Database](https://nitro.build/guide/database) or [NuxtHub SQL Database](https://hub.nuxt.com/docs/
|
|
213
|
+
- Add a database to store users, see [Nitro SQL Database](https://nitro.build/guide/database) or [NuxtHub SQL Database](https://hub.nuxt.com/docs/database)
|
|
214
214
|
- Let user signup with email & password using [password hashing](https://github.com/atinux/nuxt-auth-utils?tab=readme-ov-file#password-hashing)
|
|
215
215
|
- Add support for [WebAuthn / Passkeys](https://github.com/atinux/nuxt-auth-utils?tab=readme-ov-file#webauthn-passkey)
|
|
216
216
|
|
|
@@ -238,12 +238,19 @@ export default defineNuxtConfig({
|
|
|
238
238
|
|
|
239
239
|
## payloadExtraction
|
|
240
240
|
|
|
241
|
-
|
|
241
|
+
Controls how payload data is delivered for prerendered and cached (ISR/SWR) pages.
|
|
242
|
+
|
|
243
|
+
- `'client'` - Payload is inlined in HTML for the initial server render, and extracted to `_payload.json` files for client-side navigation. This avoids a separate network request on initial load while still enabling efficient client-side navigation.
|
|
244
|
+
- `true` - Payload is extracted to a separate `_payload.json` file for both the initial server render and client-side navigation.
|
|
245
|
+
- `false` - Payload extraction is disabled entirely. Payload is always inlined in HTML and no `_payload.json` files are generated.
|
|
246
|
+
|
|
247
|
+
The default is `true`, or `'client'` when `compatibilityVersion: 5` is set.
|
|
242
248
|
|
|
243
249
|
```ts twoslash [nuxt.config.ts]
|
|
244
250
|
export default defineNuxtConfig({
|
|
245
251
|
experimental: {
|
|
246
|
-
|
|
252
|
+
// Inline payload in HTML, extract for client-side navigation only
|
|
253
|
+
payloadExtraction: 'client',
|
|
247
254
|
},
|
|
248
255
|
})
|
|
249
256
|
```
|
|
@@ -253,7 +260,7 @@ Payload extraction also works for routes using ISR (Incremental Static Regenerat
|
|
|
253
260
|
```ts twoslash [nuxt.config.ts]
|
|
254
261
|
export default defineNuxtConfig({
|
|
255
262
|
experimental: {
|
|
256
|
-
payloadExtraction:
|
|
263
|
+
payloadExtraction: 'client',
|
|
257
264
|
},
|
|
258
265
|
routeRules: {
|
|
259
266
|
// Payload files will be generated for these cached routes
|
|
@@ -303,11 +310,27 @@ export default defineNuxtConfig({
|
|
|
303
310
|
})
|
|
304
311
|
```
|
|
305
312
|
|
|
313
|
+
You can also pass an object to configure [view transition types](/docs/4.x/getting-started/transitions#view-transition-types), which allow different CSS animations based on the type of navigation:
|
|
314
|
+
|
|
315
|
+
```ts twoslash [nuxt.config.ts]
|
|
316
|
+
export default defineNuxtConfig({
|
|
317
|
+
experimental: {
|
|
318
|
+
viewTransition: {
|
|
319
|
+
enabled: true,
|
|
320
|
+
types: ['slide'],
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
})
|
|
324
|
+
```
|
|
325
|
+
|
|
306
326
|
:link-example{to="https://stackblitz.com/edit/nuxt-view-transitions?file=app.vue" target="_blank"}
|
|
307
327
|
|
|
308
328
|
::read-more{icon="i-simple-icons-mdnwebdocs" to="https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API" target="_blank"}
|
|
309
329
|
Read more about the **View Transition API**.
|
|
310
330
|
::
|
|
331
|
+
::read-more{icon="i-simple-icons-google" to="https://developer.chrome.com/blog/view-transitions-update-io24" target="_blank"}
|
|
332
|
+
Read more about the **View Transition API**.
|
|
333
|
+
::
|
|
311
334
|
|
|
312
335
|
## writeEarlyHints
|
|
313
336
|
|
|
@@ -370,7 +393,11 @@ Out of the box, this will enable typed usage of [`navigateTo`](/docs/4.x/api/uti
|
|
|
370
393
|
You can even get typed params within a page by using `const route = useRoute('route-name')`.
|
|
371
394
|
|
|
372
395
|
::important
|
|
373
|
-
If you use `pnpm` without `shamefully-hoist=true`, you will need to
|
|
396
|
+
If you use `pnpm` without `shamefully-hoist=true`, you will need to add `unplugin-vue-router` as a hoist pattern in your `pnpm-workspace.yaml` in order for this feature to work.
|
|
397
|
+
```yaml
|
|
398
|
+
publicHoistPattern:
|
|
399
|
+
- "unplugin-vue-router"
|
|
400
|
+
```
|
|
374
401
|
::
|
|
375
402
|
|
|
376
403
|
:video-accordion{title="Watch a video from Daniel Roe explaining type-safe routing in Nuxt" videoId="SXk-L19gTZk"}
|
|
@@ -603,6 +630,30 @@ But in order to auto-import it, you would need to use `SomeFolderMyComponent`.
|
|
|
603
630
|
|
|
604
631
|
By setting `experimental.normalizeComponentNames`, these two values match, and Vue will generate a component name that matches the Nuxt pattern for component naming.
|
|
605
632
|
|
|
633
|
+
## normalizePageNames
|
|
634
|
+
|
|
635
|
+
Ensure that page component names match their route names. This sets the `__name` property on page components so that Vue's `<KeepAlive>` can correctly identify them by name.
|
|
636
|
+
|
|
637
|
+
By default, Vue assigns component names based on the filename. For example, `pages/foo/index.vue` and `pages/bar/index.vue` would both have the component name `index`. This makes name-based `<KeepAlive>` filtering unreliable because multiple pages share the same name.
|
|
638
|
+
|
|
639
|
+
With `normalizePageNames` enabled, page components are named after their route (e.g. `foo` and `bar`), so you can use `<KeepAlive>` with `include`/`exclude` without manually adding `defineOptions({ name: '...' })` to each page.
|
|
640
|
+
|
|
641
|
+
This flag is enabled when `future.compatibilityVersion` is set to `5` or higher, but you can disable this feature:
|
|
642
|
+
|
|
643
|
+
```ts twoslash [nuxt.config.ts]
|
|
644
|
+
export default defineNuxtConfig({
|
|
645
|
+
experimental: {
|
|
646
|
+
normalizePageNames: false,
|
|
647
|
+
},
|
|
648
|
+
})
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
```vue [app.vue]
|
|
652
|
+
<template>
|
|
653
|
+
<NuxtPage :keepalive="{ include: ['foo'] }" />
|
|
654
|
+
</template>
|
|
655
|
+
```
|
|
656
|
+
|
|
606
657
|
## spaLoadingTemplateLocation
|
|
607
658
|
|
|
608
659
|
When rendering a client-only page (with `ssr: false`), we optionally render a loading screen (from `~/spa-loading-template.html`).
|
|
@@ -722,7 +773,9 @@ export default defineNuxtConfig({
|
|
|
722
773
|
|
|
723
774
|
## decorators
|
|
724
775
|
|
|
725
|
-
This option enables
|
|
776
|
+
This option enables decorator syntax across your entire Nuxt/Nitro app.
|
|
777
|
+
|
|
778
|
+
When using the Vite builder (default), decorators are lowered via [Babel](https://babeljs.io/) using [`@babel/plugin-proposal-decorators`](https://babeljs.io/docs/babel-plugin-proposal-decorators). When using the webpack or rspack builders, decorators are lowered via [esbuild](https://github.com/evanw/esbuild/releases/tag/v0.21.3).
|
|
726
779
|
|
|
727
780
|
For a long time, TypeScript has had support for decorators via `compilerOptions.experimentalDecorators`. This implementation predated the TC39 standardization process. Now, decorators are a [Stage 3 Proposal](https://github.com/tc39/proposal-decorators), and supported without special configuration in TS 5.0+ (see https://github.com/microsoft/TypeScript/pull/52582 and https://devblogs.microsoft.com/typescript/announcing-typescript-5-0-beta/#decorators).
|
|
728
781
|
|
|
@@ -742,6 +795,24 @@ export default defineNuxtConfig({
|
|
|
742
795
|
})
|
|
743
796
|
```
|
|
744
797
|
|
|
798
|
+
When using the Vite builder or the Nitro server build, you will need to install additional Babel packages as dev dependencies:
|
|
799
|
+
|
|
800
|
+
::code-group
|
|
801
|
+
```bash [npm]
|
|
802
|
+
npm install -D @babel/plugin-proposal-decorators @babel/plugin-syntax-jsx
|
|
803
|
+
```
|
|
804
|
+
```bash [pnpm]
|
|
805
|
+
pnpm add -D @babel/plugin-proposal-decorators @babel/plugin-syntax-jsx
|
|
806
|
+
```
|
|
807
|
+
```bash [yarn]
|
|
808
|
+
yarn add -D @babel/plugin-proposal-decorators @babel/plugin-syntax-jsx
|
|
809
|
+
```
|
|
810
|
+
::
|
|
811
|
+
|
|
812
|
+
::tip
|
|
813
|
+
Nuxt will prompt you to install these automatically if they are not already present.
|
|
814
|
+
::
|
|
815
|
+
|
|
745
816
|
```ts [app/app.vue]
|
|
746
817
|
function something (_method: () => unknown) {
|
|
747
818
|
return () => 'decorated'
|
|
@@ -778,11 +849,16 @@ export default defineNuxtConfig({
|
|
|
778
849
|
useAsyncData: {
|
|
779
850
|
deep: true,
|
|
780
851
|
},
|
|
852
|
+
useState: {
|
|
853
|
+
resetOnClear: true,
|
|
854
|
+
},
|
|
781
855
|
},
|
|
782
856
|
},
|
|
783
857
|
})
|
|
784
858
|
```
|
|
785
859
|
|
|
860
|
+
The `useState.resetOnClear` option controls whether [`clearNuxtState`](/docs/4.x/api/utils/clear-nuxt-state) resets state to its initial value (provided by the `init` function of [`useState`](/docs/4.x/api/composables/use-state)) instead of setting it to `undefined`. This defaults to `true` with `compatibilityVersion: 5`.
|
|
861
|
+
|
|
786
862
|
## purgeCachedData
|
|
787
863
|
|
|
788
864
|
Whether to clean up Nuxt static and asyncData caches on route navigation.
|
|
@@ -57,7 +57,7 @@ Explore all available App hooks.
|
|
|
57
57
|
|
|
58
58
|
These hooks are available for [server plugins](/docs/4.x/directory-structure/server#server-plugins) to hook into Nitro's runtime behavior.
|
|
59
59
|
|
|
60
|
-
```ts [
|
|
60
|
+
```ts [~~/server/plugins/test.ts]
|
|
61
61
|
export default defineNitroPlugin((nitroApp) => {
|
|
62
62
|
nitroApp.hooks.hook('render:html', (html, { event }) => {
|
|
63
63
|
console.log('render:html', html)
|
|
@@ -52,3 +52,7 @@ To achieve full customization, you can implement your own one based on [its sour
|
|
|
52
52
|
::callout
|
|
53
53
|
You can hook into the underlying announcer instance using [the `useRouteAnnouncer` composable](/docs/4.x/api/composables/use-route-announcer), which allows you to set a custom announcement message.
|
|
54
54
|
::
|
|
55
|
+
|
|
56
|
+
::callout
|
|
57
|
+
For announcing in-page content changes (form validation, toast notifications, loading states, etc.), use the [`<NuxtAnnouncer>`](/docs/4.x/api/components/nuxt-announcer) component with the [`useAnnouncer`](/docs/4.x/api/composables/use-announcer) composable instead.
|
|
58
|
+
::
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: '<NuxtAnnouncer>'
|
|
3
|
+
description: 'The <NuxtAnnouncer> component adds a hidden element to announce dynamic content changes to assistive technologies.'
|
|
4
|
+
links:
|
|
5
|
+
- label: Source
|
|
6
|
+
icon: i-simple-icons-github
|
|
7
|
+
to: https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/components/nuxt-announcer.ts
|
|
8
|
+
size: xs
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
::important
|
|
12
|
+
This component is available in Nuxt v3.17+.
|
|
13
|
+
::
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
Add `<NuxtAnnouncer/>` in your [`app.vue`](/docs/4.x/directory-structure/app/app) or [`app/layouts/`](/docs/4.x/directory-structure/app/layouts) to enable announcing dynamic content changes to screen readers. This is useful for form validation, toast notifications, loading states, and other in-page updates.
|
|
18
|
+
|
|
19
|
+
```vue [app/app.vue]
|
|
20
|
+
<template>
|
|
21
|
+
<NuxtAnnouncer />
|
|
22
|
+
<NuxtRouteAnnouncer />
|
|
23
|
+
<NuxtLayout>
|
|
24
|
+
<NuxtPage />
|
|
25
|
+
</NuxtLayout>
|
|
26
|
+
</template>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Then use the [`useAnnouncer`](/docs/4.x/api/composables/use-announcer) composable anywhere in your app to announce messages:
|
|
30
|
+
|
|
31
|
+
```vue [app/pages/contact.vue]
|
|
32
|
+
<script setup lang="ts">
|
|
33
|
+
const { polite, assertive } = useAnnouncer()
|
|
34
|
+
|
|
35
|
+
async function submitForm () {
|
|
36
|
+
try {
|
|
37
|
+
await $fetch('/api/contact', { method: 'POST', body: formData })
|
|
38
|
+
polite('Message sent successfully')
|
|
39
|
+
} catch (error) {
|
|
40
|
+
assertive('Error: Failed to send message')
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
</script>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Slots
|
|
47
|
+
|
|
48
|
+
You can pass custom HTML or components through the announcer's default slot.
|
|
49
|
+
|
|
50
|
+
```vue
|
|
51
|
+
<template>
|
|
52
|
+
<NuxtAnnouncer>
|
|
53
|
+
<template #default="{ message }">
|
|
54
|
+
<p>{{ message }}</p>
|
|
55
|
+
</template>
|
|
56
|
+
</NuxtAnnouncer>
|
|
57
|
+
</template>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Props
|
|
61
|
+
|
|
62
|
+
- `atomic`: Controls if screen readers announce only changes or the entire content. Set to true for full content readouts on updates, false for changes only. (default `true`)
|
|
63
|
+
- `politeness`: Sets the default urgency for screen reader announcements: `off` (disable the announcement), `polite` (waits for silence), or `assertive` (interrupts immediately). (default `polite`)
|
|
64
|
+
|
|
65
|
+
## Differences from `<NuxtRouteAnnouncer>`
|
|
66
|
+
|
|
67
|
+
| Aspect | `<NuxtRouteAnnouncer>` | `<NuxtAnnouncer>` |
|
|
68
|
+
|--------|------------------------|-------------------|
|
|
69
|
+
| **Purpose** | Announces route/page changes | Announces any dynamic content |
|
|
70
|
+
| **Trigger** | Automatic on navigation | Manual via `polite()`/`assertive()` |
|
|
71
|
+
| **Message source** | Page `<title>` | Developer-provided |
|
|
72
|
+
| **atomic default** | `false` | `true` |
|
|
73
|
+
|
|
74
|
+
::callout
|
|
75
|
+
This component is optional. :br
|
|
76
|
+
To achieve full customization, you can implement your own one based on [its source code](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/components/nuxt-announcer.ts).
|
|
77
|
+
::
|
|
78
|
+
|
|
79
|
+
::callout
|
|
80
|
+
You can hook into the underlying announcer instance using [the `useAnnouncer` composable](/docs/4.x/api/composables/use-announcer), which allows you to set custom announcement messages.
|
|
81
|
+
::
|
|
@@ -86,6 +86,35 @@ console.log(layoutCustomProps.title) // I am a custom layout
|
|
|
86
86
|
</script>
|
|
87
87
|
```
|
|
88
88
|
|
|
89
|
+
## Layout Props from Page Meta
|
|
90
|
+
|
|
91
|
+
When using [`definePageMeta`](/docs/4.x/api/utils/define-page-meta) with the object syntax for `layout`, props are automatically passed to the layout component. The layout can receive them with `defineProps`:
|
|
92
|
+
|
|
93
|
+
```vue [app/pages/dashboard.vue]
|
|
94
|
+
<script setup lang="ts">
|
|
95
|
+
definePageMeta({
|
|
96
|
+
layout: {
|
|
97
|
+
name: 'admin',
|
|
98
|
+
props: {
|
|
99
|
+
sidebar: true,
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
})
|
|
103
|
+
</script>
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
```vue [app/layouts/admin.vue]
|
|
107
|
+
<script setup lang="ts">
|
|
108
|
+
const props = defineProps<{
|
|
109
|
+
sidebar?: boolean
|
|
110
|
+
}>()
|
|
111
|
+
</script>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
::read-more{to="/docs/4.x/directory-structure/app/layouts#passing-props-to-layouts"}
|
|
115
|
+
Read more about passing props to layouts.
|
|
116
|
+
::
|
|
117
|
+
|
|
89
118
|
## Transitions
|
|
90
119
|
|
|
91
120
|
`<NuxtLayout />` renders incoming content via `<slot />`, which is then wrapped around Vue’s `<Transition />` component to activate layout transition. For this to work as expected, it is recommended that `<NuxtLayout />` is **not** the root element of the page component.
|