@nuxt/docs-nightly 4.5.1-29752449.30d24817 → 4.5.2-2610052030-052d82e
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/01.introduction.md +8 -8
- package/1.getting-started/02.installation.md +2 -4
- package/1.getting-started/03.configuration.md +16 -16
- package/1.getting-started/04.views.md +8 -8
- package/1.getting-started/05.assets.md +10 -10
- package/1.getting-started/06.styling.md +10 -10
- package/1.getting-started/07.routing.md +15 -15
- package/1.getting-started/08.seo-meta.md +15 -15
- package/1.getting-started/09.transitions.md +10 -10
- package/1.getting-started/10.data-fetching.md +61 -47
- package/1.getting-started/11.state-management.md +11 -11
- package/1.getting-started/12.error-handling.md +20 -18
- package/1.getting-started/13.server.md +7 -7
- package/1.getting-started/14.layers.md +6 -6
- package/1.getting-started/15.prerendering.md +22 -28
- package/1.getting-started/16.deployment.md +22 -6
- package/1.getting-started/17.testing.md +200 -21
- package/1.getting-started/18.upgrade.md +78 -278
- package/2.directory-structure/0.nuxt.md +2 -2
- package/2.directory-structure/0.output.md +3 -3
- package/2.directory-structure/1.app/1.assets.md +3 -3
- package/2.directory-structure/1.app/1.components.md +26 -109
- package/2.directory-structure/1.app/1.composables.md +5 -5
- package/2.directory-structure/1.app/1.layouts.md +10 -10
- package/2.directory-structure/1.app/1.middleware.md +14 -10
- package/2.directory-structure/1.app/1.pages.md +25 -20
- package/2.directory-structure/1.app/1.plugins.md +7 -7
- package/2.directory-structure/1.app/1.utils.md +9 -5
- package/2.directory-structure/1.app/3.app-config.md +7 -7
- package/2.directory-structure/1.app/3.app.md +6 -6
- package/2.directory-structure/1.app/3.error.md +1 -1
- package/2.directory-structure/1.content.md +1 -1
- package/2.directory-structure/1.layers.md +13 -13
- package/2.directory-structure/1.modules.md +3 -3
- package/2.directory-structure/1.node_modules.md +1 -1
- package/2.directory-structure/1.server.md +33 -14
- package/2.directory-structure/1.shared.md +7 -5
- package/2.directory-structure/1.test.md +25 -0
- package/2.directory-structure/2.env.md +7 -7
- package/2.directory-structure/2.nuxtignore.md +3 -3
- package/2.directory-structure/2.nuxtrc.md +3 -3
- package/2.directory-structure/3.nuxt-config.md +2 -2
- package/2.directory-structure/3.tsconfig.md +21 -11
- package/2.directory-structure/index.md +29 -25
- package/3.guide/0.index.md +6 -6
- package/3.guide/1.concepts/1.rendering.md +76 -12
- package/3.guide/1.concepts/10.vuejs-development.md +73 -8
- package/3.guide/1.concepts/2.nuxt-lifecycle.md +18 -18
- package/3.guide/1.concepts/3.auto-imports.md +11 -11
- package/3.guide/1.concepts/4.server-engine.md +6 -6
- package/3.guide/1.concepts/5.modules.md +4 -4
- package/3.guide/1.concepts/6.server-components.md +198 -0
- package/3.guide/1.concepts/7.esm.md +5 -5
- package/3.guide/1.concepts/8.typescript.md +9 -9
- package/3.guide/2.best-practices/accessibility.md +155 -0
- package/3.guide/2.best-practices/hydration.md +5 -5
- package/3.guide/2.best-practices/performance.md +13 -9
- package/3.guide/3.ai/1.mcp.md +104 -19
- package/3.guide/4.modules/2.module-anatomy.md +5 -5
- package/3.guide/4.modules/3.recipes-basics.md +8 -8
- package/3.guide/4.modules/4.module-dependencies.md +1 -1
- package/3.guide/4.modules/5.recipes-advanced.md +6 -8
- package/3.guide/4.modules/6.testing.md +2 -2
- package/3.guide/4.modules/7.best-practices.md +2 -2
- package/3.guide/4.modules/9.server-compatibility.md +215 -0
- package/3.guide/4.modules/index.md +9 -9
- package/3.guide/5.recipes/1.custom-routing.md +15 -11
- package/3.guide/5.recipes/2.vite-plugin.md +3 -3
- package/3.guide/5.recipes/3.custom-usefetch.md +5 -5
- package/3.guide/5.recipes/4.sessions-and-authentication.md +2 -2
- package/3.guide/5.recipes/5.mostly-static-sites.md +127 -0
- package/3.guide/6.going-further/1.events.md +1 -1
- package/3.guide/6.going-further/1.experimental-features.md +254 -22
- package/3.guide/6.going-further/1.features.md +42 -6
- package/3.guide/6.going-further/1.internals.md +10 -10
- package/3.guide/6.going-further/10.runtime-config.md +28 -6
- package/3.guide/6.going-further/11.nightly-release-channel.md +1 -1
- package/3.guide/6.going-further/2.hooks.md +6 -6
- package/3.guide/6.going-further/4.kit.md +4 -4
- package/3.guide/6.going-further/5.server-imports.md +221 -0
- package/3.guide/6.going-further/6.nuxt-app.md +7 -7
- package/3.guide/6.going-further/7.layers.md +13 -13
- package/3.guide/6.going-further/8.builders.md +38 -14
- package/4.api/1.components/1.nuxt-client-fallback.md +1 -1
- package/4.api/1.components/10.nuxt-picture.md +1 -1
- package/4.api/1.components/11.teleports.md +1 -1
- package/4.api/1.components/12.nuxt-route-announcer.md +5 -3
- package/4.api/1.components/14.nuxt-announcer.md +3 -3
- package/4.api/1.components/2.nuxt-page.md +5 -5
- package/4.api/1.components/3.nuxt-layout.md +8 -8
- package/4.api/1.components/4.nuxt-link.md +6 -4
- package/4.api/1.components/5.nuxt-loading-indicator.md +3 -3
- package/4.api/1.components/6.nuxt-error-boundary.md +1 -1
- package/4.api/1.components/8.nuxt-island.md +5 -5
- package/4.api/2.composables/create-use-async-data.md +51 -8
- package/4.api/2.composables/create-use-fetch.md +126 -9
- package/4.api/2.composables/use-announcer.md +3 -3
- package/4.api/2.composables/use-app-config.md +1 -1
- package/4.api/2.composables/use-async-data.md +19 -11
- package/4.api/2.composables/use-cookie.md +22 -6
- package/4.api/2.composables/use-error.md +1 -1
- package/4.api/2.composables/use-fetch.md +14 -10
- package/4.api/2.composables/use-head-safe.md +2 -2
- package/4.api/2.composables/use-head.md +2 -2
- package/4.api/2.composables/use-hydration.md +1 -1
- package/4.api/2.composables/use-layout.md +1 -1
- package/4.api/2.composables/use-lazy-async-data.md +8 -8
- package/4.api/2.composables/use-lazy-fetch.md +11 -11
- package/4.api/2.composables/use-loading-indicator.md +2 -2
- package/4.api/2.composables/use-nuxt-app.md +13 -13
- package/4.api/2.composables/use-nuxt-data.md +1 -1
- package/4.api/2.composables/use-preview-mode.md +3 -3
- package/4.api/2.composables/use-request-event.md +29 -3
- package/4.api/2.composables/use-request-fetch.md +2 -2
- package/4.api/2.composables/use-request-header.md +1 -1
- package/4.api/2.composables/use-request-headers.md +1 -1
- package/4.api/2.composables/use-request-url.md +1 -1
- package/4.api/2.composables/use-response-header.md +2 -2
- package/4.api/2.composables/use-route-announcer.md +4 -2
- package/4.api/2.composables/use-route.md +2 -2
- package/4.api/2.composables/use-router.md +5 -5
- package/4.api/2.composables/use-runtime-config.md +5 -5
- package/4.api/2.composables/use-runtime-hook.md +1 -1
- package/4.api/2.composables/use-seo-meta.md +3 -3
- package/4.api/2.composables/use-server-seo-meta.md +5 -5
- package/4.api/2.composables/use-state.md +4 -4
- package/4.api/3.utils/$fetch.md +5 -5
- package/4.api/3.utils/abort-navigation.md +1 -1
- package/4.api/3.utils/add-route-middleware.md +2 -2
- package/4.api/3.utils/call-once.md +3 -3
- package/4.api/3.utils/clear-error.md +2 -2
- package/4.api/3.utils/clear-nuxt-data.md +1 -1
- package/4.api/3.utils/clear-nuxt-state.md +2 -2
- package/4.api/3.utils/create-error.md +19 -1
- package/4.api/3.utils/define-nuxt-plugin.md +2 -2
- package/4.api/3.utils/define-nuxt-route-middleware.md +3 -3
- package/4.api/3.utils/define-page-meta.md +12 -12
- package/4.api/3.utils/define-route-rules.md +4 -4
- package/4.api/3.utils/define-use-async-data-addon.md +57 -0
- package/4.api/3.utils/define-use-fetch-addon.md +168 -0
- package/4.api/3.utils/navigate-to.md +6 -2
- package/4.api/3.utils/prefetch-components.md +1 -1
- package/4.api/3.utils/preload-route-components.md +2 -2
- package/4.api/3.utils/prerender-routes.md +3 -3
- package/4.api/3.utils/refresh-cookie.md +1 -1
- package/4.api/3.utils/refresh-nuxt-data.md +3 -3
- package/4.api/3.utils/reload-nuxt-app.md +1 -1
- package/4.api/3.utils/set-page-layout.md +1 -1
- package/4.api/3.utils/set-response-status.md +2 -2
- package/4.api/3.utils/show-error.md +3 -3
- package/4.api/3.utils/update-app-config.md +2 -2
- package/4.api/4.commands/README.md +9 -0
- package/4.api/5.kit/1.modules.md +3 -3
- package/4.api/5.kit/10.runtime-config.md +1 -1
- package/4.api/5.kit/10.templates.md +42 -0
- package/4.api/5.kit/12.nitro.md +59 -18
- package/4.api/5.kit/14.logging.md +56 -2
- package/4.api/5.kit/15.builder.md +1 -1
- package/4.api/5.kit/18.dependencies.md +90 -0
- package/4.api/5.kit/2.programmatic.md +78 -1
- package/4.api/5.kit/3.compatibility.md +2 -2
- package/4.api/5.kit/4.autoimports.md +13 -13
- package/4.api/5.kit/5.components.md +3 -3
- package/4.api/5.kit/6.context.md +2 -2
- package/4.api/5.kit/7.pages.md +35 -4
- package/4.api/5.kit/8.layout.md +1 -1
- package/4.api/6.advanced/1.hooks.md +6 -3
- package/4.api/6.nuxt-config.md +44 -21
- package/4.api/index.md +7 -7
- package/5.community/2.getting-help.md +1 -1
- package/5.community/3.reporting-bugs.md +1 -1
- package/5.community/4.contribution.md +14 -12
- package/5.community/5.framework-contribution.md +4 -4
- package/5.community/6.roadmap.md +6 -6
- package/6.bridge/1.overview.md +12 -12
- package/6.bridge/2.typescript.md +1 -1
- package/6.bridge/3.bridge-composition-api.md +6 -6
- package/6.bridge/4.plugins-and-middleware.md +4 -4
- package/6.bridge/5.nuxt3-compatible-api.md +9 -9
- package/6.bridge/6.meta.md +5 -5
- package/6.bridge/7.runtime-config.md +1 -1
- package/6.bridge/8.nitro.md +1 -1
- package/6.bridge/9.vite.md +1 -1
- package/7.migration/1.overview.md +2 -2
- package/7.migration/10.bundling.md +1 -1
- package/7.migration/11.server.md +3 -3
- package/7.migration/2.configuration.md +6 -6
- package/7.migration/20.module-authors.md +7 -7
- package/7.migration/3.auto-imports.md +3 -3
- package/7.migration/4.meta.md +4 -4
- package/7.migration/5.plugins-and-middleware.md +3 -3
- package/7.migration/6.pages-and-layouts.md +13 -13
- package/7.migration/7.component-options.md +15 -15
- package/7.migration/8.runtime-config.md +4 -4
- package/README.md +1 -1
- package/errors/e8007.md +28 -0
- package/package.json +2 -2
- package/4.api/4.commands/.navigation.yml +0 -3
- package/4.api/4.commands/add.md +0 -112
- package/4.api/4.commands/analyze.md +0 -42
- package/4.api/4.commands/build-module.md +0 -42
- package/4.api/4.commands/build.md +0 -48
- package/4.api/4.commands/cleanup.md +0 -38
- package/4.api/4.commands/dev.md +0 -61
- package/4.api/4.commands/devtools.md +0 -38
- package/4.api/4.commands/generate.md +0 -43
- package/4.api/4.commands/info.md +0 -33
- package/4.api/4.commands/init.md +0 -50
- package/4.api/4.commands/module.md +0 -84
- package/4.api/4.commands/prepare.md +0 -41
- package/4.api/4.commands/preview.md +0 -44
- package/4.api/4.commands/test.md +0 -40
- package/4.api/4.commands/typecheck.md +0 -44
- package/4.api/4.commands/upgrade.md +0 -37
|
@@ -16,7 +16,7 @@ It is shipped with many features:
|
|
|
16
16
|
|
|
17
17
|
## API Layer
|
|
18
18
|
|
|
19
|
-
Server [API endpoints](/docs/
|
|
19
|
+
Server [API endpoints](/docs/directory-structure/server#server-routes) and [Middleware](/docs/directory-structure/server#server-middleware) are added by Nitro that internally uses [h3](https://github.com/h3js/h3).
|
|
20
20
|
|
|
21
21
|
Key features include:
|
|
22
22
|
|
|
@@ -26,15 +26,15 @@ Key features include:
|
|
|
26
26
|
|
|
27
27
|
Check out [the h3 docs](https://github.com/h3js/h3) for more information.
|
|
28
28
|
|
|
29
|
-
::read-more{to="/docs/
|
|
29
|
+
::read-more{to="/docs/directory-structure/server#server-routes"}
|
|
30
30
|
Learn more about the API layer in the `server/` directory.
|
|
31
31
|
::
|
|
32
32
|
|
|
33
33
|
## Direct API Calls
|
|
34
34
|
|
|
35
|
-
Nitro allows 'direct' calling of routes via the globally-available [`$fetch`](/docs/
|
|
35
|
+
Nitro allows 'direct' calling of routes via the globally-available [`$fetch`](/docs/api/utils/dollarfetch) helper. This will make an API call to the server if run on the browser, but will directly call the relevant function if run on the server, **saving an additional API call**.
|
|
36
36
|
|
|
37
|
-
[`$fetch`](/docs/
|
|
37
|
+
[`$fetch`](/docs/api/utils/dollarfetch) API is using [ofetch](https://github.com/unjs/ofetch), with key features including:
|
|
38
38
|
|
|
39
39
|
- Automatic parsing of JSON responses (with access to raw response if needed)
|
|
40
40
|
- Request body and params are automatically handled, with correct `Content-Type` headers
|
|
@@ -45,7 +45,7 @@ For more information on `$fetch` features, check out [ofetch](https://github.com
|
|
|
45
45
|
|
|
46
46
|
When using API routes (or middleware), Nitro will generate typings for these routes as long as you are returning a value instead of using `res.end()` to send a response.
|
|
47
47
|
|
|
48
|
-
You can access these types when using [`$fetch()`](/docs/
|
|
48
|
+
You can access these types when using [`$fetch()`](/docs/api/utils/dollarfetch) or [`useFetch()`](/docs/api/composables/use-fetch).
|
|
49
49
|
|
|
50
50
|
## Standalone Server
|
|
51
51
|
|
|
@@ -53,7 +53,7 @@ Nitro produces a standalone server dist that is independent of `node_modules`.
|
|
|
53
53
|
|
|
54
54
|
The server in Nuxt 2 is not standalone and requires part of Nuxt core to be involved by running `nuxt start` (with the [`nuxt-start`](https://www.npmjs.com/package/nuxt-start) or [`nuxt`](https://www.npmjs.com/package/nuxt) distributions) or custom programmatic usage, which is fragile and prone to breakage and not suitable for serverless and service worker environments.
|
|
55
55
|
|
|
56
|
-
Nuxt generates this dist when running `nuxt build` into a [`.output`](/docs/
|
|
56
|
+
Nuxt generates this dist when running `nuxt build` into a [`.output`](/docs/directory-structure/output) directory.
|
|
57
57
|
|
|
58
58
|
The output contains runtime code to run your Nuxt server in any environment (including experimental browser service workers!) and serve your static files, making it a true hybrid framework for the JAMstack. In addition, Nuxt implements a native storage layer, supporting multi-source drivers and local assets.
|
|
59
59
|
|
|
@@ -7,7 +7,7 @@ description: "Nuxt provides a module system to extend the framework core and sim
|
|
|
7
7
|
|
|
8
8
|
When developing production-grade applications with Nuxt you might find that the framework's core functionality is not enough. Nuxt can be extended with configuration options and plugins, but maintaining these customizations across multiple projects can be tedious, repetitive and time-consuming. On the other hand, supporting every project's needs out of the box would make Nuxt very complex and hard to use.
|
|
9
9
|
|
|
10
|
-
This is one of the reasons why Nuxt provides a module system that makes it possible to extend the core. Nuxt modules are async functions that sequentially run when starting Nuxt in development mode using [`nuxt dev`](/docs/
|
|
10
|
+
This is one of the reasons why Nuxt provides a module system that makes it possible to extend the core. Nuxt modules are async functions that sequentially run when starting Nuxt in development mode using [`nuxt dev`](/docs/api/commands/dev) or building a project for production with [`nuxt build`](/docs/api/commands/build). They can override templates, configure webpack loaders, add CSS libraries, and perform many other useful tasks.
|
|
11
11
|
|
|
12
12
|
Best of all, Nuxt modules can be distributed in npm packages. This makes it possible for them to be reused across projects and shared with the community, helping create an ecosystem of high-quality add-ons.
|
|
13
13
|
|
|
@@ -17,7 +17,7 @@ Explore Nuxt Modules
|
|
|
17
17
|
|
|
18
18
|
## Add Nuxt Modules
|
|
19
19
|
|
|
20
|
-
Once you have installed the modules you can add them to your [`nuxt.config.ts`](/docs/
|
|
20
|
+
Once you have installed the modules you can add them to your [`nuxt.config.ts`](/docs/directory-structure/nuxt-config) file under the `modules` property. Module developers usually provide additional steps and details for usage.
|
|
21
21
|
|
|
22
22
|
```ts twoslash [nuxt.config.ts]
|
|
23
23
|
export default defineNuxtConfig({
|
|
@@ -52,10 +52,10 @@ export default defineNuxtConfig({
|
|
|
52
52
|
})
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
:read-more{to="/docs/
|
|
55
|
+
:read-more{to="/docs/guide/going-further/layers#disabling-modules-from-layers"}
|
|
56
56
|
|
|
57
57
|
## Create a Nuxt Module
|
|
58
58
|
|
|
59
59
|
Everyone has the opportunity to develop modules and we cannot wait to see what you will build.
|
|
60
60
|
|
|
61
|
-
:read-more{to="/docs/
|
|
61
|
+
:read-more{to="/docs/guide/modules" title="Module Author Guide"}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: 'Server Components'
|
|
3
|
+
description: Render individual components on the server only, keeping their JavaScript out of your client bundle.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Nuxt renders your app on the server by default, but then it ships the JavaScript for every component to the browser and hydrates the whole page. For content-heavy components (markdown rendering, syntax highlighting, CMS output) that never change on the client, this is wasted work: the user downloads, parses and executes code whose only job is to reproduce HTML that is already on the page.
|
|
7
|
+
|
|
8
|
+
Server components (also called island components) invert this. A server component is rendered on the server, its HTML is embedded in the page, and none of its JavaScript is sent to the client. Its dependencies (a markdown parser, a highlighting library) stay on the server too.
|
|
9
|
+
|
|
10
|
+
::tip{icon="i-lucide-newspaper" to="https://roe.dev/blog/nuxt-server-components" target="_blank"}
|
|
11
|
+
Read Daniel Roe's guide to Nuxt Server Components.
|
|
12
|
+
::
|
|
13
|
+
|
|
14
|
+
:video-accordion{title="Watch Learn Vue video about Nuxt Server Components" videoId="u1yyXe86xJM"}
|
|
15
|
+
|
|
16
|
+
## Enabling Server Components
|
|
17
|
+
|
|
18
|
+
Component islands are controlled by [`experimental.componentIslands`](/docs/guide/going-further/experimental-features#componentislands). The default value is `'auto'`, which enables the feature automatically as soon as your app contains a server component or island, so in most cases you do not need any configuration.
|
|
19
|
+
|
|
20
|
+
Set the option explicitly if you want remote islands or selective client hydration:
|
|
21
|
+
|
|
22
|
+
```ts twoslash [nuxt.config.ts]
|
|
23
|
+
export default defineNuxtConfig({
|
|
24
|
+
experimental: {
|
|
25
|
+
componentIslands: {
|
|
26
|
+
selectiveClient: true, // or 'deep', to enable `nuxt-client`
|
|
27
|
+
remoteIsland: false, // allow rendering islands from a remote source
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
})
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
::read-more{icon="i-simple-icons-github" to="https://github.com/nuxt/nuxt/issues/19772" target="_blank"}
|
|
34
|
+
Server components are still marked experimental. You can follow the roadmap on GitHub.
|
|
35
|
+
::
|
|
36
|
+
|
|
37
|
+
## `.server.vue` Components
|
|
38
|
+
|
|
39
|
+
Add the `.server` suffix to a component to make it a standalone server component:
|
|
40
|
+
|
|
41
|
+
```bash [Directory Structure]
|
|
42
|
+
-| app/
|
|
43
|
+
---| components/
|
|
44
|
+
-----| HighlightedMarkdown.server.vue
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Use it like any other component:
|
|
48
|
+
|
|
49
|
+
```vue [app/pages/example.vue]
|
|
50
|
+
<template>
|
|
51
|
+
<div>
|
|
52
|
+
<!--
|
|
53
|
+
rendered on the server; the markdown parsing and highlighting
|
|
54
|
+
libraries are not included in your client bundle
|
|
55
|
+
-->
|
|
56
|
+
<HighlightedMarkdown markdown="# Headline" />
|
|
57
|
+
</div>
|
|
58
|
+
</template>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Components inside `~/components/islands/` are also registered as islands and can be rendered with [`<NuxtIsland>`](/docs/api/components/nuxt-island) directly, for example `<NuxtIsland name="MyIsland" />` for `~/components/islands/MyIsland.vue`.
|
|
62
|
+
|
|
63
|
+
You can also pair a `.server.vue` component with a `.client.vue` component of the same name for [separate server and client implementations](/docs/directory-structure/app/components#paired-with-a-client-component). In that case the component is not an island: the client half hydrates normally.
|
|
64
|
+
|
|
65
|
+
::warning
|
|
66
|
+
Server components (and islands) must have a single root element. (HTML comments are considered elements as well.)
|
|
67
|
+
::
|
|
68
|
+
|
|
69
|
+
## How Islands Are Rendered
|
|
70
|
+
|
|
71
|
+
Server components use [`<NuxtIsland>`](/docs/api/components/nuxt-island) under the hood. Rendering an island issues a request to a dedicated island endpoint, which:
|
|
72
|
+
|
|
73
|
+
- creates a **new, isolated Vue app** on the server to render just that component
|
|
74
|
+
- creates an 'island context' that you can access via `nuxtApp.ssrContext.islandContext` inside the island
|
|
75
|
+
- runs your plugins again, unless they set `env: { islands: false }` (object-syntax plugins)
|
|
76
|
+
|
|
77
|
+
Because the island is isolated from the rest of your app:
|
|
78
|
+
|
|
79
|
+
- you cannot share state (provide/inject, Pinia, `useState`) between the page and the island; pass data via props instead
|
|
80
|
+
- [`useRoute()`](/docs/api/composables/use-route) inside an island reflects the island's own request, not the page the user is on. If an island needs route information, pass it in explicitly, either as props or via the `context` prop on `<NuxtIsland>` (read inside the island from `nuxtApp.ssrContext.islandContext`)
|
|
81
|
+
- route middleware does not run when rendering islands
|
|
82
|
+
|
|
83
|
+
Props are serialized and sent as **GET query parameters**. This makes island responses cacheable, but it also means:
|
|
84
|
+
|
|
85
|
+
- props must be JSON-serializable
|
|
86
|
+
- props are limited by URL length, so avoid passing large amounts of data
|
|
87
|
+
- props may be visible in server access logs, CDN caches and `Referer` headers
|
|
88
|
+
|
|
89
|
+
::note
|
|
90
|
+
Because props come from the request (URL query or body), treat them as untrusted input. Nuxt rejects the props most likely to leak through unintentionally: a top-level `as` that the island does not declare (an undeclared prop falls through as an attribute onto the island's root), and, with `vue.runtimeCompiler` enabled, a `template` anywhere in the props. Beyond that, avoid feeding props you have not validated into dynamic component resolution (`<component :is>`{lang=vue}, `h()`, `resolveDynamicComponent()`, or a polymorphic `as` / `asChild` prop), since a string can resolve to any registered component or HTML element.
|
|
91
|
+
|
|
92
|
+
Props a component does not declare fall through as attributes onto its single root element, so an island whose root is a polymorphic component (e.g. from `reka-ui` / `@nuxt/ui`) can receive attributes you did not bind. Set `defineOptions({ inheritAttrs: false })`{lang=ts} on such islands, or declare the props you accept.
|
|
93
|
+
|
|
94
|
+
To switch components based on caller input, map a discriminator through an allowlist of imported components rather than passing the raw prop:
|
|
95
|
+
|
|
96
|
+
```vue
|
|
97
|
+
<script setup lang="ts">
|
|
98
|
+
import type { Component } from 'vue'
|
|
99
|
+
import CardA from './CardA.vue'
|
|
100
|
+
import CardB from './CardB.vue'
|
|
101
|
+
|
|
102
|
+
const props = defineProps<{ variant: string }>()
|
|
103
|
+
const allowed: Record<string, Component> = { a: CardA, b: CardB }
|
|
104
|
+
const component = allowed[props.variant] ?? CardA
|
|
105
|
+
</script>
|
|
106
|
+
|
|
107
|
+
<template>
|
|
108
|
+
<component :is="component" />
|
|
109
|
+
</template>
|
|
110
|
+
```
|
|
111
|
+
::
|
|
112
|
+
|
|
113
|
+
Changing an island's props triggers a network request that re-renders the component on the server and updates its HTML in place.
|
|
114
|
+
|
|
115
|
+
::read-more{to="/docs/api/components/nuxt-island"}
|
|
116
|
+
Read the full `<NuxtIsland>` API documentation, including props, slots, events and known limitations.
|
|
117
|
+
::
|
|
118
|
+
|
|
119
|
+
## Selective Hydration with `nuxt-client`
|
|
120
|
+
|
|
121
|
+
An island is static by default, but you can hydrate individual components inside it by adding the `nuxt-client` attribute. This requires `experimental.componentIslands.selectiveClient` to be enabled.
|
|
122
|
+
|
|
123
|
+
```vue [app/components/ServerWithClient.server.vue]
|
|
124
|
+
<template>
|
|
125
|
+
<div>
|
|
126
|
+
<HighlightedMarkdown markdown="# Headline" />
|
|
127
|
+
<!-- Counter will be loaded and hydrated client-side -->
|
|
128
|
+
<Counter
|
|
129
|
+
nuxt-client
|
|
130
|
+
:count="5"
|
|
131
|
+
/>
|
|
132
|
+
</div>
|
|
133
|
+
</template>
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The component marked with `nuxt-client` is server-rendered as part of the island, then hydrated by the main client app. Only its chunk is shipped to the client; the rest of the island remains static.
|
|
137
|
+
|
|
138
|
+
Setting `selectiveClient: 'deep'` additionally allows passing slots to `nuxt-client` components. Those slots are rendered on the server and are **not interactive** on the client.
|
|
139
|
+
|
|
140
|
+
::warning
|
|
141
|
+
Use `nuxt-client` only on local `.vue` SFCs. Built-ins like [`<NuxtLink>`](/docs/api/components/nuxt-link) skip the islands transform. After client navigation you may see `Failed to locate Teleport target`, or the link disappears with no error. Wrap the built-in in your own `.vue` file and put `nuxt-client` on that wrapper. See [#29251](https://github.com/nuxt/nuxt/issues/29251) and [#26002](https://github.com/nuxt/nuxt/issues/26002).
|
|
142
|
+
::
|
|
143
|
+
|
|
144
|
+
## Slots
|
|
145
|
+
|
|
146
|
+
Slots can be passed to an island component if declared in the island. Slot content is provided by the parent, so it belongs to the main client app and **is** interactive (it is wrapped in a `<div>` with `display: contents;`).
|
|
147
|
+
|
|
148
|
+
`<NuxtIsland>` reserves the `#fallback` slot to specify content rendered before the island loads (when `lazy` is set) or when fetching the island fails.
|
|
149
|
+
|
|
150
|
+
## The Client Navigation Round Trip
|
|
151
|
+
|
|
152
|
+
On the initial server-rendered page load, islands are rendered inline and there is no extra request. On **client-side navigation**, however, each island on the destination page must be fetched from the server (you can see these requests in the network tab). This has real costs:
|
|
153
|
+
|
|
154
|
+
- islands block on a network round trip during navigation, unless you pass the `lazy` prop (with a `#fallback` slot) to render them non-blockingly
|
|
155
|
+
- an app with many islands per page makes many requests per navigation
|
|
156
|
+
|
|
157
|
+
Islands work best on pages that are reached by full page loads (content and marketing pages) or when their number per page is small. If a component needs to update frequently on the client, an island is probably the wrong tool.
|
|
158
|
+
|
|
159
|
+
When a `<NuxtLink>` to a [server-only page](/docs/directory-structure/app/pages#server-only-pages) is prefetched, its island response is prefetched too, so the round trip happens before the user navigates rather than during the navigation. The prefetched response is cached against the island's name, props and context, which is the same key the island uses when it mounts. A failed prefetch is not fatal: the island is fetched again on mount.
|
|
160
|
+
|
|
161
|
+
::note
|
|
162
|
+
This applies to the island that renders a whole `.server.vue` page. Islands used within a page are still fetched when they mount.
|
|
163
|
+
::
|
|
164
|
+
|
|
165
|
+
## Prerendering and Caching
|
|
166
|
+
|
|
167
|
+
Islands play well with static and cached rendering:
|
|
168
|
+
|
|
169
|
+
- during prerendering (`nuxt generate` or `prerender` route rules), island responses are cached, so identical islands (same name, props and context) are rendered once and reused
|
|
170
|
+
- because props travel as GET query parameters, island responses can also be cached by your server or CDN at the island endpoint level
|
|
171
|
+
- two instances of the same island with the same props share a single server render and payload entry
|
|
172
|
+
|
|
173
|
+
Note that island responses being keyed only on name, props and context is exactly what keeps them cacheable independently of the page they appear on; this is also why they cannot see the current route (see above).
|
|
174
|
+
|
|
175
|
+
If you are building a mostly-static site, islands combine well with `prerender` and `noScripts` route rules.
|
|
176
|
+
|
|
177
|
+
::read-more{to="/docs/guide/recipes/mostly-static-sites"}
|
|
178
|
+
See the mostly-static site recipe for combining prerendering, `noScripts`, islands and lazy hydration.
|
|
179
|
+
::
|
|
180
|
+
|
|
181
|
+
One interaction to be aware of: island slots and `nuxt-client` components rely on a small inline script to relocate teleported content into place before hydration. On routes rendered with [`noScripts`](/docs/guide/going-further/features#noscripts), that script is omitted, so fully interactive `nuxt-client` components will not hydrate there. Plain static islands are unaffected.
|
|
182
|
+
|
|
183
|
+
## Current Limitations
|
|
184
|
+
|
|
185
|
+
Server components are experimental, and some rough edges are tracked in open issues:
|
|
186
|
+
|
|
187
|
+
- Most features for server-only and island components, such as slots and `nuxt-client` components, are only available for single file components.
|
|
188
|
+
- Using islands can significantly increase the number of chunks generated at build time ([#34855](https://github.com/nuxt/nuxt/issues/34855)).
|
|
189
|
+
- With webpack and Rspack, scoped `:slotted()` styles in server component slots can fail because server and client builds may generate different scope IDs ([#31510](https://github.com/nuxt/nuxt/issues/31510)).
|
|
190
|
+
- Template refs cannot reference elements inside a server component from the parent ([#31512](https://github.com/nuxt/nuxt/issues/31512)).
|
|
191
|
+
- `inject`/`provide` does not cross the island boundary, so injecting from the page into a standalone server component does not work ([#22751](https://github.com/nuxt/nuxt/issues/22751)).
|
|
192
|
+
- Server components rendered via the auto-generated wrapper do not expose load and error events; use `<NuxtIsland>` directly if you need its `error` event and `refresh()` method ([#25744](https://github.com/nuxt/nuxt/issues/25744)).
|
|
193
|
+
- [`useId`](https://vuejs.org/api/composition-api-helpers#useid) has known limitations inside islands; see the [`<NuxtIsland>` documentation](/docs/api/components/nuxt-island#known-limitations).
|
|
194
|
+
- Each nested island adds extra overhead, so be careful when nesting islands within other islands.
|
|
195
|
+
|
|
196
|
+
::read-more{to="/docs/directory-structure/app/components#server-components"}
|
|
197
|
+
Read more about server component file conventions in the components directory documentation.
|
|
198
|
+
::
|
|
@@ -116,7 +116,7 @@ const { named } = pkg;
|
|
|
116
116
|
|
|
117
117
|
## Troubleshooting ESM Issues
|
|
118
118
|
|
|
119
|
-
If you encounter these errors, the issue is almost certainly with the upstream library. They need to [fix their library](/docs/
|
|
119
|
+
If you encounter these errors, the issue is almost certainly with the upstream library. They need to [fix their library](/docs/guide/concepts/esm#library-author-guide) to support being imported by Node.
|
|
120
120
|
|
|
121
121
|
### Transpiling Libraries
|
|
122
122
|
|
|
@@ -197,7 +197,7 @@ import { default as pkg } from 'cjs-pkg'
|
|
|
197
197
|
import('cjs-pkg').then(m => m.default || m).then(console.log)
|
|
198
198
|
```
|
|
199
199
|
|
|
200
|
-
For handling more complex situations and more safety, we recommend
|
|
200
|
+
For handling more complex situations and more safety, we recommend [mlly](https://github.com/unjs/mlly), which can preserve named exports.
|
|
201
201
|
|
|
202
202
|
```js
|
|
203
203
|
import { interopDefault } from 'mlly'
|
|
@@ -280,16 +280,16 @@ const someFile = require.resolve('./lib/foo.js')
|
|
|
280
280
|
```
|
|
281
281
|
|
|
282
282
|
```js [After]
|
|
283
|
-
import {
|
|
283
|
+
import { resolveModulePath } from 'exsolve'
|
|
284
284
|
|
|
285
|
-
const someFile =
|
|
285
|
+
const someFile = resolveModulePath('my-lib', { from: import.meta.url })
|
|
286
286
|
```
|
|
287
287
|
|
|
288
288
|
::
|
|
289
289
|
|
|
290
290
|
### Best Practices
|
|
291
291
|
|
|
292
|
-
- Prefer named exports rather than default export. This helps reduce CJS conflicts. (see [Default exports](/docs/
|
|
292
|
+
- Prefer named exports rather than default export. This helps reduce CJS conflicts. (see [Default exports](/docs/guide/concepts/esm#default-exports) section)
|
|
293
293
|
|
|
294
294
|
- Avoid depending on Node.js built-ins and CommonJS or Node.js-only dependencies as much as possible to make your library usable in Browsers and Edge Workers without needing Nitro polyfills.
|
|
295
295
|
|
|
@@ -5,7 +5,7 @@ description: "Nuxt is fully typed and provides helpful shortcuts to ensure you h
|
|
|
5
5
|
|
|
6
6
|
## Type-checking
|
|
7
7
|
|
|
8
|
-
By default, Nuxt doesn't check types when you run [`nuxt dev`](/docs/
|
|
8
|
+
By default, Nuxt doesn't check types when you run [`nuxt dev`](/docs/api/commands/dev) or [`nuxt build`](/docs/api/commands/build), for performance reasons.
|
|
9
9
|
|
|
10
10
|
To enable type-checking at build or development time, install `vue-tsc` and `typescript` as development dependency:
|
|
11
11
|
|
|
@@ -33,13 +33,13 @@ To enable type-checking at build or development time, install `vue-tsc` and `typ
|
|
|
33
33
|
|
|
34
34
|
::
|
|
35
35
|
|
|
36
|
-
Then, run [`nuxt typecheck`](/docs/
|
|
36
|
+
Then, run [`nuxt typecheck`](/docs/api/commands/typecheck) command to check your types:
|
|
37
37
|
|
|
38
38
|
```bash [Terminal]
|
|
39
39
|
npx nuxt typecheck
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
To enable type-checking at build or development time, you can also use the [`typescript.typeCheck`](/docs/
|
|
42
|
+
To enable type-checking at build or development time, you can also use the [`typescript.typeCheck`](/docs/api/nuxt-config#typecheck) option in your `nuxt.config` file:
|
|
43
43
|
|
|
44
44
|
```ts twoslash [nuxt.config.ts]
|
|
45
45
|
export default defineNuxtConfig({
|
|
@@ -51,12 +51,12 @@ export default defineNuxtConfig({
|
|
|
51
51
|
|
|
52
52
|
## Auto-generated Types
|
|
53
53
|
|
|
54
|
-
Nuxt projects rely on auto-generated types to work properly. These types are stored in the [`.nuxt`](/docs/
|
|
54
|
+
Nuxt projects rely on auto-generated types to work properly. These types are stored in the [`.nuxt`](/docs/directory-structure/nuxt) directory and are generated when you run the dev server or build your application. You can also generate these files manually by running `nuxt prepare`.
|
|
55
55
|
|
|
56
|
-
The generated `tsconfig.json` files inside the [`.nuxt`](/docs/
|
|
56
|
+
The generated `tsconfig.json` files inside the [`.nuxt`](/docs/directory-structure/nuxt) directory include **recommended basic TypeScript configuration** for your project, references to [auto-imports](/docs/guide/concepts/auto-imports), [API route types](/docs/guide/concepts/server-engine#typed-api-routes), path aliases like `#imports`, `~/file`, or `#build/file`, and more.
|
|
57
57
|
|
|
58
58
|
::warning
|
|
59
|
-
Nuxt relies on this configuration, and [Nuxt modules](/docs/
|
|
59
|
+
Nuxt relies on this configuration, and [Nuxt modules](/docs/guide/modules) can extend it as well. For this reason, it is not recommended to modify your `tsconfig.json` file directly, as doing so could overwrite important settings. Instead, extend it via `nuxt.config.ts`. [Learn more about extending the configuration here](/docs/directory-structure/tsconfig).
|
|
60
60
|
::
|
|
61
61
|
|
|
62
62
|
::tip{icon="i-lucide-video" to="https://youtu.be/umLI7SlPygY" target="_blank"}
|
|
@@ -79,7 +79,7 @@ When you run `nuxt dev`, `nuxt build` or `nuxt prepare`, Nuxt will generate mult
|
|
|
79
79
|
Each of these files is configured to reference the appropriate dependencies and provide optimal type-checking for their specific context.
|
|
80
80
|
|
|
81
81
|
::note
|
|
82
|
-
For backward compatibility, Nuxt still generates `.nuxt/tsconfig.json`. However, we recommend using [TypeScript project references](/docs/
|
|
82
|
+
For backward compatibility, Nuxt still generates `.nuxt/tsconfig.json`. However, we recommend using [TypeScript project references](/docs/directory-structure/tsconfig) with the new configuration files (`.nuxt/tsconfig.app.json`, `.nuxt/tsconfig.server.json`, etc.) for better type safety and performance. This legacy file will be removed in a future version of Nuxt.
|
|
83
83
|
::
|
|
84
84
|
|
|
85
85
|
### Benefits of Project References
|
|
@@ -99,7 +99,7 @@ Similarly:
|
|
|
99
99
|
- For the `server` context, place the augmentation file in the `server/` directory.
|
|
100
100
|
- For types that are **shared between the app and server**, place the file in the `shared/` directory.
|
|
101
101
|
|
|
102
|
-
::read-more{to="/docs/
|
|
102
|
+
::read-more{to="/docs/guide/modules/recipes-advanced#extend-typescript-config"}
|
|
103
103
|
Read more about augmenting specific type contexts from **files outside those contexts** in the Module Author Guide.
|
|
104
104
|
::
|
|
105
105
|
|
|
@@ -107,7 +107,7 @@ Read more about augmenting specific type contexts from **files outside those con
|
|
|
107
107
|
|
|
108
108
|
TypeScript comes with certain checks to give you more safety and analysis of your program.
|
|
109
109
|
|
|
110
|
-
[Strict checks](https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html#getting-stricter-checks) are enabled by default in Nuxt when the [`typescript.typeCheck`](/docs/
|
|
110
|
+
[Strict checks](https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html#getting-stricter-checks) are enabled by default in Nuxt when the [`typescript.typeCheck`](/docs/guide/concepts/typescript#type-checking) option is enabled to give you greater type safety.
|
|
111
111
|
|
|
112
112
|
If you are currently converting your codebase to TypeScript, you may want to temporarily disable strict checks by setting `strict` to `false` in your `nuxt.config`:
|
|
113
113
|
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
---
|
|
2
|
+
navigation.title: 'Accessibility'
|
|
3
|
+
title: Nuxt accessibility
|
|
4
|
+
description: Best practices for accessibility in Nuxt apps.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Most of what makes an app accessible is not specific to Nuxt: color contrast, form semantics, and ARIA work the same here as in any Vue or plain HTML application, and the [resources](#useful-resources) at the end of this guide cover them well.
|
|
8
|
+
|
|
9
|
+
What Nuxt does change is navigation. Once your app has hydrated, it routes on the client, so the document is never reloaded and the browser no longer announces a new page or resets focus for you. Nuxt ships with features that fill some of that gap, and a few conventions cover the rest. This guide outlines best practices for handling that.
|
|
10
|
+
|
|
11
|
+
::tip
|
|
12
|
+
[`@nuxt/a11y`](https://github.com/nuxt/a11y) surfaces accessibility problems in your components while you develop, alongside the practices below. It is in alpha, so expect its API to change.
|
|
13
|
+
::
|
|
14
|
+
|
|
15
|
+
## Route Announcements
|
|
16
|
+
|
|
17
|
+
Screen readers announce a full page load by themselves, but they have no way of knowing that a client-side navigation happened. [`<NuxtRouteAnnouncer>`](/docs/api/components/nuxt-route-announcer) solves this by rendering a hidden live region and writing the new page title into it after every navigation:
|
|
18
|
+
|
|
19
|
+
```vue [app.vue]
|
|
20
|
+
<template>
|
|
21
|
+
<NuxtRouteAnnouncer />
|
|
22
|
+
<NuxtPage />
|
|
23
|
+
</template>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The announcer reads the title that Unhead rendered, so it is only as useful as your titles are. If two routes share the same `<title>`, users hear nothing on the way between them.
|
|
27
|
+
|
|
28
|
+
When you need to announce something else, or to change how urgently it is announced, use [`useRouteAnnouncer`](/docs/api/composables/use-route-announcer):
|
|
29
|
+
|
|
30
|
+
```vue [app/pages/search.vue]
|
|
31
|
+
<script setup lang="ts">
|
|
32
|
+
const { set } = useRouteAnnouncer()
|
|
33
|
+
const { data: results } = await useFetch('/api/search')
|
|
34
|
+
|
|
35
|
+
watch(results, (results) => {
|
|
36
|
+
set(`${results?.length ?? 0} results found`)
|
|
37
|
+
})
|
|
38
|
+
</script>
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
:read-more{title="NuxtRouteAnnouncer" to="/docs/api/components/nuxt-route-announcer"}
|
|
42
|
+
|
|
43
|
+
For in-page updates that are not navigations, such as form validation or toasts, use [`<NuxtAnnouncer>`](/docs/api/components/nuxt-announcer) with [`useAnnouncer`](/docs/api/composables/use-announcer) instead.
|
|
44
|
+
|
|
45
|
+
## Page Titles
|
|
46
|
+
|
|
47
|
+
Because the route announcer follows the document title, giving every route a distinct title is the single most valuable thing you can do. Set a global template in `app.vue` and let each page fill in its own part:
|
|
48
|
+
|
|
49
|
+
```vue [app.vue]
|
|
50
|
+
<script setup lang="ts">
|
|
51
|
+
useHead({
|
|
52
|
+
titleTemplate: title => title ? `${title} - Nuxt` : 'Nuxt',
|
|
53
|
+
})
|
|
54
|
+
</script>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
```vue [app/pages/about.vue]
|
|
58
|
+
<script setup lang="ts">
|
|
59
|
+
useHead({
|
|
60
|
+
title: 'About us',
|
|
61
|
+
})
|
|
62
|
+
</script>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
If your titles come from route metadata rather than from the page itself, you can read [`definePageMeta`](/docs/directory-structure/app/pages#page-metadata) values from [`useRoute`](/docs/api/composables/use-route) in a layout.
|
|
66
|
+
|
|
67
|
+
:read-more{title="SEO and Meta" to="/docs/getting-started/seo-meta#dynamic-title"}
|
|
68
|
+
|
|
69
|
+
## Links
|
|
70
|
+
|
|
71
|
+
Use [`<NuxtLink>`](/docs/api/components/nuxt-link) for in-app navigation. It renders a real `<a href="...">`, which means it is focusable, appears in the tab order, and works with middle-click and "open in new tab", all of which you would have to reimplement on a `<div>` with a `@click` handler calling `navigateTo`.
|
|
72
|
+
|
|
73
|
+
```vue
|
|
74
|
+
<template>
|
|
75
|
+
<NuxtLink to="/about">About page</NuxtLink>
|
|
76
|
+
</template>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
In a menu or a set of breadcrumbs, the link matching the current route already exposes `aria-current="page"`, so assistive technology can tell which item you are on. Where a different token describes the relationship better, such as a step in a multi-page form, set [`ariaCurrentValue`](/docs/api/components/nuxt-link#routerlink):
|
|
80
|
+
|
|
81
|
+
```vue
|
|
82
|
+
<template>
|
|
83
|
+
<NuxtLink
|
|
84
|
+
to="/checkout/payment"
|
|
85
|
+
aria-current-value="step"
|
|
86
|
+
>Payment</NuxtLink>
|
|
87
|
+
</template>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Links to files in your `public/` directory, or to another app on the same origin, are not routes that Vue Router knows about. Mark them as [`external`](/docs/api/components/nuxt-link#handling-static-file-and-cross-app-links) so the browser performs a real navigation instead of failing to match a route.
|
|
91
|
+
|
|
92
|
+
:read-more{title="NuxtLink" to="/docs/api/components/nuxt-link"}
|
|
93
|
+
|
|
94
|
+
## Focus Management
|
|
95
|
+
|
|
96
|
+
After a client-side navigation, focus stays where it was, which is usually the link the user just activated. Vue Router does not move it and neither does Nuxt, so a keyboard user can end up tabbing through the whole header again to reach the content that just changed.
|
|
97
|
+
|
|
98
|
+
A skip link as the first tab stop of your app is the conventional fix, and it helps on the initial page load too:
|
|
99
|
+
|
|
100
|
+
```vue [app.vue]
|
|
101
|
+
<template>
|
|
102
|
+
<a
|
|
103
|
+
class="skip-link"
|
|
104
|
+
href="#main"
|
|
105
|
+
>Skip to main content</a>
|
|
106
|
+
<AppHeader />
|
|
107
|
+
<main
|
|
108
|
+
id="main"
|
|
109
|
+
tabindex="-1"
|
|
110
|
+
>
|
|
111
|
+
<NuxtPage />
|
|
112
|
+
</main>
|
|
113
|
+
</template>
|
|
114
|
+
|
|
115
|
+
<style>
|
|
116
|
+
.skip-link {
|
|
117
|
+
position: absolute;
|
|
118
|
+
left: -9999px;
|
|
119
|
+
}
|
|
120
|
+
.skip-link:focus {
|
|
121
|
+
position: static;
|
|
122
|
+
}
|
|
123
|
+
</style>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
`<main>` is not focusable on its own, so it needs `tabindex="-1"` to accept focus from the skip link or from a script. Use `-1` rather than a positive value, which would move the element in the tab order and surprise everyone else.
|
|
127
|
+
|
|
128
|
+
If it suits your app, you can go further and move focus to the main region after every navigation from a plugin:
|
|
129
|
+
|
|
130
|
+
```ts [app/plugins/focus-main.client.ts]
|
|
131
|
+
export default defineNuxtPlugin(() => {
|
|
132
|
+
useRouter().afterEach((to, from) => {
|
|
133
|
+
if (to.path === from.path) {
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
nextTick(() => document.getElementById('main')?.focus())
|
|
137
|
+
})
|
|
138
|
+
})
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
::tip
|
|
142
|
+
Navigate around your app with the keyboard alone. Tabbing from the skip link into `<main>` after a couple of navigations will surface most focus problems quickly.
|
|
143
|
+
::
|
|
144
|
+
|
|
145
|
+
## Scroll Behavior
|
|
146
|
+
|
|
147
|
+
Nuxt scrolls to the top on a new route, restores the previous position when the user goes back, and scrolls to hash targets. If you need something different, such as smooth scrolling or a different offset, configure [`scrollBehaviorType`](/docs/guide/recipes/custom-routing#scroll-behavior-for-hash-links) or write your own `scrollBehavior` in [`router.options.ts`](/docs/guide/recipes/custom-routing#router-options). Bear in mind that smooth scrolling should respect the user's `prefers-reduced-motion` setting.
|
|
148
|
+
|
|
149
|
+
:read-more{title="Custom routing" to="/docs/guide/recipes/custom-routing"}
|
|
150
|
+
|
|
151
|
+
## Useful Resources
|
|
152
|
+
|
|
153
|
+
- [Web Accessibility Initiative (WAI)](https://www.w3.org/WAI/)
|
|
154
|
+
- [MDN: Accessibility](https://developer.mozilla.org/en-US/docs/Web/Accessibility)
|
|
155
|
+
- [Understanding WCAG 2.1](https://www.w3.org/WAI/WCAG21/Understanding/)
|
|
@@ -47,7 +47,7 @@ const userTheme = localStorage.getItem('theme') || 'light'
|
|
|
47
47
|
</script>
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
**Solution**: You can use [`useCookie`](/docs/
|
|
50
|
+
**Solution**: You can use [`useCookie`](/docs/api/composables/use-cookie):
|
|
51
51
|
|
|
52
52
|
```html
|
|
53
53
|
<template>
|
|
@@ -144,12 +144,12 @@ const greeting = hour < 12 ? 'Good morning' : 'Good afternoon'
|
|
|
144
144
|
</script>
|
|
145
145
|
```
|
|
146
146
|
|
|
147
|
-
**Solution**: Use [`NuxtTime`](/docs/
|
|
147
|
+
**Solution**: Use [`NuxtTime`](/docs/api/components/nuxt-time) component or handle it client-side:
|
|
148
148
|
|
|
149
149
|
```html
|
|
150
150
|
<template>
|
|
151
151
|
<div>
|
|
152
|
-
<NuxtTime :
|
|
152
|
+
<NuxtTime :datetime="new Date()" format="HH:mm" />
|
|
153
153
|
</div>
|
|
154
154
|
</template>
|
|
155
155
|
```
|
|
@@ -178,8 +178,8 @@ onMounted(() => {
|
|
|
178
178
|
|
|
179
179
|
## In Summary
|
|
180
180
|
|
|
181
|
-
1. **Use SSR-friendly composables**: [`useFetch`](/docs/
|
|
182
|
-
2. **Wrap client-only code**: Use [`ClientOnly`](/docs/
|
|
181
|
+
1. **Use SSR-friendly composables**: [`useFetch`](/docs/api/composables/use-fetch), [`useAsyncData`](/docs/api/composables/use-async-data), [`useState`](/docs/api/composables/use-state)
|
|
182
|
+
2. **Wrap client-only code**: Use [`ClientOnly`](/docs/api/components/client-only) component for browser-specific content
|
|
183
183
|
3. **Consistent data sources**: Ensure server and client uses the same data
|
|
184
184
|
4. **Avoid side effects in setup**: Move browser-dependent code to `onMounted`
|
|
185
185
|
|