@nuxt/docs 0.0.0 → 3.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (224) hide show
  1. package/.navigation.yml +2 -0
  2. package/1.getting-started/.navigation.yml +3 -0
  3. package/1.getting-started/01.introduction.md +81 -0
  4. package/1.getting-started/02.installation.md +109 -0
  5. package/1.getting-started/03.configuration.md +226 -0
  6. package/1.getting-started/04.views.md +163 -0
  7. package/1.getting-started/05.assets.md +48 -0
  8. package/1.getting-started/06.styling.md +565 -0
  9. package/1.getting-started/07.routing.md +149 -0
  10. package/1.getting-started/08.seo-meta.md +360 -0
  11. package/1.getting-started/09.transitions.md +473 -0
  12. package/1.getting-started/10.data-fetching.md +795 -0
  13. package/1.getting-started/11.state-management.md +223 -0
  14. package/1.getting-started/12.error-handling.md +233 -0
  15. package/1.getting-started/13.server.md +94 -0
  16. package/1.getting-started/14.layers.md +92 -0
  17. package/1.getting-started/15.prerendering.md +194 -0
  18. package/1.getting-started/16.deployment.md +130 -0
  19. package/1.getting-started/17.testing.md +728 -0
  20. package/1.getting-started/18.upgrade.md +997 -0
  21. package/2.guide/.navigation.yml +2 -0
  22. package/2.guide/0.index.md +22 -0
  23. package/2.guide/1.concepts/.navigation.yml +3 -0
  24. package/2.guide/1.concepts/1.auto-imports.md +205 -0
  25. package/2.guide/1.concepts/10.nuxt-lifecycle.md +141 -0
  26. package/2.guide/1.concepts/2.vuejs-development.md +103 -0
  27. package/2.guide/1.concepts/3.rendering.md +255 -0
  28. package/2.guide/1.concepts/4.server-engine.md +62 -0
  29. package/2.guide/1.concepts/5.modules.md +48 -0
  30. package/2.guide/1.concepts/7.esm.md +299 -0
  31. package/2.guide/1.concepts/8.typescript.md +97 -0
  32. package/2.guide/1.concepts/9.code-style.md +22 -0
  33. package/2.guide/2.directory-structure/.navigation.yml +3 -0
  34. package/2.guide/2.directory-structure/0.nuxt.md +20 -0
  35. package/2.guide/2.directory-structure/0.output.md +18 -0
  36. package/2.guide/2.directory-structure/1.assets.md +16 -0
  37. package/2.guide/2.directory-structure/1.components.md +608 -0
  38. package/2.guide/2.directory-structure/1.composables.md +121 -0
  39. package/2.guide/2.directory-structure/1.content.md +64 -0
  40. package/2.guide/2.directory-structure/1.layouts.md +180 -0
  41. package/2.guide/2.directory-structure/1.middleware.md +209 -0
  42. package/2.guide/2.directory-structure/1.modules.md +66 -0
  43. package/2.guide/2.directory-structure/1.node_modules.md +12 -0
  44. package/2.guide/2.directory-structure/1.pages.md +440 -0
  45. package/2.guide/2.directory-structure/1.plugins.md +299 -0
  46. package/2.guide/2.directory-structure/1.public.md +27 -0
  47. package/2.guide/2.directory-structure/1.server.md +546 -0
  48. package/2.guide/2.directory-structure/1.shared.md +104 -0
  49. package/2.guide/2.directory-structure/1.utils.md +49 -0
  50. package/2.guide/2.directory-structure/2.env.md +75 -0
  51. package/2.guide/2.directory-structure/2.gitignore.md +37 -0
  52. package/2.guide/2.directory-structure/2.nuxtignore.md +36 -0
  53. package/2.guide/2.directory-structure/2.nuxtrc.md +50 -0
  54. package/2.guide/2.directory-structure/3.app-config.md +177 -0
  55. package/2.guide/2.directory-structure/3.app.md +72 -0
  56. package/2.guide/2.directory-structure/3.error.md +55 -0
  57. package/2.guide/2.directory-structure/3.nuxt-config.md +34 -0
  58. package/2.guide/2.directory-structure/3.package.md +32 -0
  59. package/2.guide/2.directory-structure/3.tsconfig.md +24 -0
  60. package/2.guide/3.going-further/.navigation.yml +3 -0
  61. package/2.guide/3.going-further/1.experimental-features.md +689 -0
  62. package/2.guide/3.going-further/1.features.md +103 -0
  63. package/2.guide/3.going-further/1.internals.md +81 -0
  64. package/2.guide/3.going-further/10.runtime-config.md +174 -0
  65. package/2.guide/3.going-further/11.nightly-release-channel.md +68 -0
  66. package/2.guide/3.going-further/2.hooks.md +98 -0
  67. package/2.guide/3.going-further/3.modules.md +811 -0
  68. package/2.guide/3.going-further/4.kit.md +51 -0
  69. package/2.guide/3.going-further/6.nuxt-app.md +64 -0
  70. package/2.guide/3.going-further/7.layers.md +227 -0
  71. package/2.guide/3.going-further/9.debugging.md +115 -0
  72. package/2.guide/3.going-further/index.md +4 -0
  73. package/2.guide/4.recipes/.navigation.yml +3 -0
  74. package/2.guide/4.recipes/1.custom-routing.md +181 -0
  75. package/2.guide/4.recipes/2.vite-plugin.md +65 -0
  76. package/2.guide/4.recipes/3.custom-usefetch.md +125 -0
  77. package/2.guide/4.recipes/4.sessions-and-authentication.md +203 -0
  78. package/3.api/.navigation.yml +3 -0
  79. package/3.api/1.components/.navigation.yml +3 -0
  80. package/3.api/1.components/1.client-only.md +76 -0
  81. package/3.api/1.components/1.dev-only.md +51 -0
  82. package/3.api/1.components/1.nuxt-client-fallback.md +80 -0
  83. package/3.api/1.components/10.nuxt-picture.md +27 -0
  84. package/3.api/1.components/11.teleports.md +40 -0
  85. package/3.api/1.components/12.nuxt-route-announcer.md +56 -0
  86. package/3.api/1.components/13.nuxt-time.md +173 -0
  87. package/3.api/1.components/2.nuxt-page.md +154 -0
  88. package/3.api/1.components/3.nuxt-layout.md +156 -0
  89. package/3.api/1.components/4.nuxt-link.md +322 -0
  90. package/3.api/1.components/5.nuxt-loading-indicator.md +50 -0
  91. package/3.api/1.components/6.nuxt-error-boundary.md +65 -0
  92. package/3.api/1.components/7.nuxt-welcome.md +25 -0
  93. package/3.api/1.components/8.nuxt-island.md +70 -0
  94. package/3.api/1.components/9.nuxt-img.md +43 -0
  95. package/3.api/2.composables/.navigation.yml +3 -0
  96. package/3.api/2.composables/on-prehydrate.md +60 -0
  97. package/3.api/2.composables/use-app-config.md +19 -0
  98. package/3.api/2.composables/use-async-data.md +212 -0
  99. package/3.api/2.composables/use-cookie.md +233 -0
  100. package/3.api/2.composables/use-error.md +32 -0
  101. package/3.api/2.composables/use-fetch.md +217 -0
  102. package/3.api/2.composables/use-head-safe.md +55 -0
  103. package/3.api/2.composables/use-head.md +69 -0
  104. package/3.api/2.composables/use-hydration.md +68 -0
  105. package/3.api/2.composables/use-lazy-async-data.md +47 -0
  106. package/3.api/2.composables/use-lazy-fetch.md +55 -0
  107. package/3.api/2.composables/use-loading-indicator.md +77 -0
  108. package/3.api/2.composables/use-nuxt-app.md +294 -0
  109. package/3.api/2.composables/use-nuxt-data.md +112 -0
  110. package/3.api/2.composables/use-preview-mode.md +118 -0
  111. package/3.api/2.composables/use-request-event.md +23 -0
  112. package/3.api/2.composables/use-request-fetch.md +52 -0
  113. package/3.api/2.composables/use-request-header.md +34 -0
  114. package/3.api/2.composables/use-request-headers.md +37 -0
  115. package/3.api/2.composables/use-request-url.md +41 -0
  116. package/3.api/2.composables/use-response-header.md +48 -0
  117. package/3.api/2.composables/use-route-announcer.md +60 -0
  118. package/3.api/2.composables/use-route.md +52 -0
  119. package/3.api/2.composables/use-router.md +92 -0
  120. package/3.api/2.composables/use-runtime-config.md +142 -0
  121. package/3.api/2.composables/use-runtime-hook.md +43 -0
  122. package/3.api/2.composables/use-seo-meta.md +80 -0
  123. package/3.api/2.composables/use-server-seo-meta.md +27 -0
  124. package/3.api/2.composables/use-state.md +48 -0
  125. package/3.api/3.utils/$fetch.md +98 -0
  126. package/3.api/3.utils/.navigation.yml +3 -0
  127. package/3.api/3.utils/abort-navigation.md +73 -0
  128. package/3.api/3.utils/add-route-middleware.md +88 -0
  129. package/3.api/3.utils/call-once.md +92 -0
  130. package/3.api/3.utils/clear-error.md +29 -0
  131. package/3.api/3.utils/clear-nuxt-data.md +23 -0
  132. package/3.api/3.utils/clear-nuxt-state.md +23 -0
  133. package/3.api/3.utils/create-error.md +55 -0
  134. package/3.api/3.utils/define-nuxt-component.md +53 -0
  135. package/3.api/3.utils/define-nuxt-route-middleware.md +67 -0
  136. package/3.api/3.utils/define-page-meta.md +234 -0
  137. package/3.api/3.utils/define-route-rules.md +52 -0
  138. package/3.api/3.utils/navigate-to.md +230 -0
  139. package/3.api/3.utils/on-before-route-leave.md +11 -0
  140. package/3.api/3.utils/on-before-route-update.md +11 -0
  141. package/3.api/3.utils/on-nuxt-ready.md +25 -0
  142. package/3.api/3.utils/prefetch-components.md +28 -0
  143. package/3.api/3.utils/preload-components.md +23 -0
  144. package/3.api/3.utils/preload-route-components.md +41 -0
  145. package/3.api/3.utils/prerender-routes.md +46 -0
  146. package/3.api/3.utils/refresh-cookie.md +46 -0
  147. package/3.api/3.utils/refresh-nuxt-data.md +91 -0
  148. package/3.api/3.utils/reload-nuxt-app.md +74 -0
  149. package/3.api/3.utils/set-page-layout.md +24 -0
  150. package/3.api/3.utils/set-response-status.md +36 -0
  151. package/3.api/3.utils/show-error.md +31 -0
  152. package/3.api/3.utils/update-app-config.md +27 -0
  153. package/3.api/4.commands/.navigation.yml +3 -0
  154. package/3.api/4.commands/add.md +112 -0
  155. package/3.api/4.commands/analyze.md +41 -0
  156. package/3.api/4.commands/build-module.md +42 -0
  157. package/3.api/4.commands/build.md +46 -0
  158. package/3.api/4.commands/cleanup.md +38 -0
  159. package/3.api/4.commands/dev.md +59 -0
  160. package/3.api/4.commands/devtools.md +38 -0
  161. package/3.api/4.commands/generate.md +41 -0
  162. package/3.api/4.commands/info.md +33 -0
  163. package/3.api/4.commands/init.md +46 -0
  164. package/3.api/4.commands/module.md +84 -0
  165. package/3.api/4.commands/prepare.md +36 -0
  166. package/3.api/4.commands/preview.md +43 -0
  167. package/3.api/4.commands/typecheck.md +42 -0
  168. package/3.api/4.commands/upgrade.md +37 -0
  169. package/3.api/5.kit/.navigation.yml +3 -0
  170. package/3.api/5.kit/1.modules.md +172 -0
  171. package/3.api/5.kit/10.runtime-config.md +27 -0
  172. package/3.api/5.kit/10.templates.md +283 -0
  173. package/3.api/5.kit/11.nitro.md +409 -0
  174. package/3.api/5.kit/12.resolving.md +268 -0
  175. package/3.api/5.kit/13.logging.md +65 -0
  176. package/3.api/5.kit/14.builder.md +491 -0
  177. package/3.api/5.kit/15.examples.md +41 -0
  178. package/3.api/5.kit/2.programmatic.md +125 -0
  179. package/3.api/5.kit/3.compatibility.md +230 -0
  180. package/3.api/5.kit/4.autoimports.md +144 -0
  181. package/3.api/5.kit/5.components.md +127 -0
  182. package/3.api/5.kit/6.context.md +130 -0
  183. package/3.api/5.kit/7.pages.md +295 -0
  184. package/3.api/5.kit/8.layout.md +80 -0
  185. package/3.api/5.kit/9.plugins.md +263 -0
  186. package/3.api/6.advanced/.navigation.yml +1 -0
  187. package/3.api/6.advanced/1.hooks.md +105 -0
  188. package/3.api/6.advanced/2.import-meta.md +60 -0
  189. package/3.api/6.nuxt-config.md +12 -0
  190. package/3.api/index.md +31 -0
  191. package/5.community/.navigation.yml +3 -0
  192. package/5.community/2.getting-help.md +48 -0
  193. package/5.community/3.reporting-bugs.md +50 -0
  194. package/5.community/4.contribution.md +205 -0
  195. package/5.community/5.framework-contribution.md +142 -0
  196. package/5.community/6.roadmap.md +79 -0
  197. package/5.community/7.changelog.md +92 -0
  198. package/6.bridge/.navigation.yml +3 -0
  199. package/6.bridge/1.overview.md +137 -0
  200. package/6.bridge/10.configuration.md +96 -0
  201. package/6.bridge/2.typescript.md +46 -0
  202. package/6.bridge/3.bridge-composition-api.md +132 -0
  203. package/6.bridge/4.plugins-and-middleware.md +65 -0
  204. package/6.bridge/5.nuxt3-compatible-api.md +204 -0
  205. package/6.bridge/6.meta.md +117 -0
  206. package/6.bridge/7.runtime-config.md +38 -0
  207. package/6.bridge/8.nitro.md +102 -0
  208. package/6.bridge/9.vite.md +37 -0
  209. package/7.migration/.navigation.yml +3 -0
  210. package/7.migration/1.overview.md +24 -0
  211. package/7.migration/10.bundling.md +28 -0
  212. package/7.migration/11.server.md +17 -0
  213. package/7.migration/2.configuration.md +240 -0
  214. package/7.migration/20.module-authors.md +94 -0
  215. package/7.migration/3.auto-imports.md +18 -0
  216. package/7.migration/4.meta.md +127 -0
  217. package/7.migration/5.plugins-and-middleware.md +80 -0
  218. package/7.migration/6.pages-and-layouts.md +233 -0
  219. package/7.migration/7.component-options.md +156 -0
  220. package/7.migration/8.runtime-config.md +58 -0
  221. package/LICENSE +21 -0
  222. package/README.md +11 -0
  223. package/package.json +16 -4
  224. package/dist/.gitkeep +0 -0
@@ -0,0 +1,997 @@
1
+ ---
2
+ title: Upgrade Guide
3
+ description: 'Learn how to upgrade to the latest Nuxt version.'
4
+ navigation.icon: i-lucide-circle-arrow-up
5
+ ---
6
+
7
+ ## Upgrading Nuxt
8
+
9
+ ### Latest release
10
+
11
+ To upgrade Nuxt to the [latest release](https://github.com/nuxt/nuxt/releases), use the `nuxi upgrade` command.
12
+
13
+ ::code-group{sync="pm"}
14
+
15
+ ```bash [npm]
16
+ npx nuxi upgrade
17
+ ```
18
+
19
+ ```bash [yarn]
20
+ yarn dlx nuxi upgrade
21
+ ```
22
+
23
+ ```bash [pnpm]
24
+ pnpm dlx nuxi upgrade
25
+ ```
26
+
27
+ ```bash [bun]
28
+ bun x nuxi upgrade
29
+ ```
30
+
31
+ ::
32
+
33
+ ### Nightly Release Channel
34
+
35
+ To use the latest Nuxt build and test features before their release, read about the [nightly release channel](/docs/guide/going-further/nightly-release-channel) guide.
36
+
37
+ ::warning
38
+ The nightly release channel `latest` tag is currently tracking the Nuxt v4 branch, meaning that it is particularly likely to have breaking changes right now - be careful!
39
+
40
+ You can opt in to the 3.x branch nightly releases with `"nuxt": "npm:nuxt-nightly@3x"`.
41
+ ::
42
+
43
+ ## Testing Nuxt 4
44
+
45
+ The release date of Nuxt 4 is **to be announced**. It is dependent on having enough time after Nitro's major release to be properly tested in the community. You can follow progress towards Nitro's release in [this PR](https://github.com/nitrojs/nitro/pull/2521).
46
+
47
+ Until the release, it is possible to test many of Nuxt 4's breaking changes from Nuxt version 3.12+.
48
+
49
+ :video-accordion{title="Watch a video from Alexander Lichter showing how to opt in to Nuxt 4's breaking changes already" videoId="r4wFKlcJK6c"}
50
+
51
+ ### Opting in to Nuxt 4
52
+
53
+ First, upgrade Nuxt to the [latest release](https://github.com/nuxt/nuxt/releases).
54
+
55
+ Then you can set your `compatibilityVersion` to match Nuxt 4 behavior:
56
+
57
+ ::code-collapse
58
+ ```ts twoslash [nuxt.config.ts]
59
+ export default defineNuxtConfig({
60
+ future: {
61
+ compatibilityVersion: 4,
62
+ },
63
+ // To re-enable _all_ Nuxt v3 behavior, set the following options:
64
+ // srcDir: '.',
65
+ // dir: {
66
+ // app: 'app'
67
+ // },
68
+ // experimental: {
69
+ // scanPageMeta: 'after-resolve',
70
+ // sharedPrerenderData: false,
71
+ // compileTemplate: true,
72
+ // resetAsyncDataToUndefined: true,
73
+ // templateUtils: true,
74
+ // relativeWatchPaths: true,
75
+ // normalizeComponentNames: false,
76
+ // spaLoadingTemplateLocation: 'within',
77
+ // parseErrorData: false,
78
+ // pendingWhenIdle: true,
79
+ // defaults: {
80
+ // useAsyncData: {
81
+ // deep: true
82
+ // }
83
+ // }
84
+ // },
85
+ // features: {
86
+ // inlineStyles: true
87
+ // },
88
+ // unhead: {
89
+ // renderSSRHeadOptions: {
90
+ // omitLineBreaks: false
91
+ // }
92
+ // }
93
+ })
94
+ ```
95
+ ::
96
+
97
+ ::note
98
+ For now, you need to define the compatibility version in each layer that opts into Nuxt 4 behavior. This will not be required after Nuxt 4 is released.
99
+ ::
100
+
101
+ When you set your `compatibilityVersion` to `4`, defaults throughout your Nuxt configuration will change to opt in to Nuxt v4 behavior, but you can granularly re-enable Nuxt v3 behavior when testing, following the commented out lines above. Please file issues if so, so that we can address them in Nuxt or in the ecosystem.
102
+
103
+ Breaking or significant changes will be noted here along with migration steps for backward/forward compatibility.
104
+
105
+ ::note
106
+ This section is subject to change until the final release, so please check back here regularly if you are testing Nuxt 4 using `compatibilityVersion: 4`.
107
+ ::
108
+
109
+ ### Migrating Using Codemods
110
+
111
+ To facilitate the upgrade process, we have collaborated with the [Codemod](https://github.com/codemod-com/codemod) team to automate many migration steps with some open-source codemods.
112
+
113
+ ::note
114
+ If you encounter any issues, please report them to the Codemod team with `npx codemod feedback` 🙏
115
+ ::
116
+
117
+ For a complete list of Nuxt 4 codemods, detailed information on each, their source, and various ways to run them, visit the [Codemod Registry](https://go.codemod.com/codemod-registry).
118
+
119
+ You can run all the codemods mentioned in this guide using the following `codemod` recipe:
120
+
121
+ ::code-group
122
+
123
+ ```bash [npm]
124
+ npx codemod@latest nuxt/4/migration-recipe
125
+ ```
126
+
127
+ ```bash [yarn]
128
+ yarn dlx codemod@latest nuxt/4/migration-recipe
129
+ ```
130
+
131
+ ```bash [pnpm]
132
+ pnpm dlx codemod@latest nuxt/4/migration-recipe
133
+ ```
134
+
135
+ ```bash [bun]
136
+ bun x codemod@latest nuxt/4/migration-recipe
137
+ ```
138
+
139
+ ::
140
+
141
+ This command will execute all codemods in sequence, with the option to deselect any that you do not wish to run. Each codemod is also listed below alongside its respective change and can be executed independently.
142
+
143
+ ### New Directory Structure
144
+
145
+ 🚦 **Impact Level**: Significant
146
+
147
+ Nuxt now defaults to a new directory structure, with backwards compatibility (so if Nuxt detects you are using the old structure, such as with a top-level `pages/` directory, this new structure will not apply).
148
+
149
+ 👉 [See full RFC](https://github.com/nuxt/nuxt/issues/26444)
150
+
151
+ #### What Changed
152
+
153
+ * the new Nuxt default `srcDir` is `app/` by default, and most things are resolved from there.
154
+ * `serverDir` now defaults to `<rootDir>/server` rather than `<srcDir>/server`
155
+ * `layers/`, `modules/` and `public/` are resolved relative to `<rootDir>` by default
156
+ * if using [Nuxt Content v2.13+](https://github.com/nuxt/content/pull/2649), `content/` is resolved relative to `<rootDir>`
157
+ * 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>/`
158
+
159
+ <details>
160
+
161
+ <summary>An example v4 folder structure.</summary>
162
+
163
+ ```sh
164
+ .output/
165
+ .nuxt/
166
+ app/
167
+ assets/
168
+ components/
169
+ composables/
170
+ layouts/
171
+ middleware/
172
+ pages/
173
+ plugins/
174
+ utils/
175
+ app.config.ts
176
+ app.vue
177
+ router.options.ts
178
+ content/
179
+ layers/
180
+ modules/
181
+ node_modules/
182
+ public/
183
+ server/
184
+ api/
185
+ middleware/
186
+ plugins/
187
+ routes/
188
+ utils/
189
+ nuxt.config.ts
190
+ ```
191
+
192
+ </details>
193
+
194
+ 👉 For more details, see the [PR implementing this change](https://github.com/nuxt/nuxt/pull/27029).
195
+
196
+ #### Reasons for Change
197
+
198
+ 1. **Performance** - placing all your code in the root of your repo causes issues with `.git/` and `node_modules/` folders being scanned/included by FS watchers which can significantly delay startup on non-Mac OSes.
199
+ 1. **IDE type-safety** - `server/` and the rest of your app are running in two entirely different contexts with different global imports available, and making sure `server/` isn't _inside_ the same folder as the rest of your app is a big first step to ensuring you get good auto-completes in your IDE.
200
+
201
+ :video-accordion{title="Watch a video from Vue School on the new directory structure" videoId="1031028378" platform="vimeo"}
202
+
203
+ #### Migration Steps
204
+
205
+ 1. Create a new directory called `app/`.
206
+ 1. Move your `assets/`, `components/`, `composables/`, `layouts/`, `middleware/`, `pages/`, `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.
207
+ 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.
208
+ 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).
209
+
210
+ ::tip
211
+ You can automate this migration by running `npx codemod@latest nuxt/4/file-structure`
212
+ ::
213
+
214
+ 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.
215
+
216
+ You can also force a v3 folder structure with the following configuration:
217
+
218
+ ```ts [nuxt.config.ts]
219
+ export default defineNuxtConfig({
220
+ // This reverts the new srcDir default from `app` back to your root directory
221
+ srcDir: '.',
222
+ // This specifies the directory prefix for `app/router.options.ts` and `app/spa-loading-template.html`
223
+ dir: {
224
+ app: 'app'
225
+ }
226
+ })
227
+ ```
228
+
229
+ ### Singleton Data Fetching Layer
230
+
231
+ 🚦 **Impact Level**: Moderate
232
+
233
+ #### What Changed
234
+
235
+ Nuxt's data fetching system (`useAsyncData` and `useFetch`) has been significantly reorganized for better performance and consistency:
236
+
237
+ 1. **Shared refs for the same key**: All calls to `useAsyncData` or `useFetch` with the same key now share the same `data`, `error` and `status` refs. This means that it is important that all calls with an explicit key must not have conflicting `deep`, `transform`, `pick`, `getCachedData` or `default` options.
238
+
239
+ 2. **More control over `getCachedData`**: The `getCachedData` function is now called every time data is fetched, even if this is caused by a watcher or calling `refreshNuxtData`. (Previously, new data was always fetched and this function was not called in these cases.) To allow more control over when to use cached data and when to refetch, the function now receives a context object with the cause of the request.
240
+
241
+ 3. **Reactive key support**: You can now use computed refs, plain refs or getter functions as keys, which enables automatic data refetching (and stores data separately).
242
+
243
+ 4. **Data cleanup**: When the last component using data fetched with `useAsyncData` is unmounted, Nuxt will remove that data to avoid ever-growing memory usage.
244
+
245
+ #### Reasons for Change
246
+
247
+ These changes have been made to improve memory usage and increase consistency with loading states across calls of `useAsyncData`.
248
+
249
+ #### Migration Steps
250
+
251
+ 1. **Check for inconsistent options**: Review any components using the same key with different options or fetch functions.
252
+
253
+ ```ts
254
+ // This will now trigger a warning
255
+ const { data: users1 } = useAsyncData('users', () => $fetch('/api/users'), { deep: false })
256
+ const { data: users2 } = useAsyncData('users', () => $fetch('/api/users'), { deep: true })
257
+ ```
258
+
259
+ It may be beneficial to extract any calls to `useAsyncData` that share an explicit key (and have custom options) into their own composable:
260
+
261
+ ```ts [composables/useUserData.ts]
262
+ export function useUserData(userId: string) {
263
+ return useAsyncData(
264
+ `user-${userId}`,
265
+ () => fetchUser(userId),
266
+ {
267
+ deep: true,
268
+ transform: (user) => ({ ...user, lastAccessed: new Date() })
269
+ }
270
+ )
271
+ }
272
+ ```
273
+
274
+ 2. **Update `getCachedData` implementations**:
275
+
276
+ ```diff
277
+ useAsyncData('key', fetchFunction, {
278
+ - getCachedData: (key, nuxtApp) => {
279
+ - return cachedData[key]
280
+ - }
281
+ + getCachedData: (key, nuxtApp, ctx) => {
282
+ + // ctx.cause - can be 'initial' | 'refresh:hook' | 'refresh:manual' | 'watch'
283
+ +
284
+ + // Example: Don't use cache on manual refresh
285
+ + if (ctx.cause === 'refresh:manual') return undefined
286
+ +
287
+ + return cachedData[key]
288
+ + }
289
+ })
290
+ ```
291
+
292
+ Alternatively, for now, you can disable this behaviour with:
293
+
294
+ ```ts twoslash [nuxt.config.ts]
295
+ export default defineNuxtConfig({
296
+ experimental: {
297
+ granularCachedData: false,
298
+ purgeCachedData: false
299
+ }
300
+ })
301
+ ```
302
+
303
+ ### Deduplication of Route Metadata
304
+
305
+ 🚦 **Impact Level**: Minimal
306
+
307
+ #### What Changed
308
+
309
+ It's possible to set some route metadata using `definePageMeta`, such as the `name`, `path`, and so on. Previously these were available both on the route and on route metadata (for example, `route.name` and `route.meta.name`).
310
+
311
+ Now, they are only accessible on the route object.
312
+
313
+ #### Reasons for Change
314
+
315
+ This is a result of enabling `experimental.scanPageMeta` by default, and is a performance optimization.
316
+
317
+ #### Migration Steps
318
+
319
+ The migration should be straightforward:
320
+
321
+ ```diff
322
+ const route = useRoute()
323
+
324
+ - console.log(route.meta.name)
325
+ + console.log(route.name)
326
+ ```
327
+
328
+ ### Normalized Component Names
329
+
330
+ 🚦 **Impact Level**: Moderate
331
+
332
+ Vue will now generate component names that match the Nuxt pattern for component naming.
333
+
334
+ #### What Changed
335
+
336
+ By default, if you haven't set it manually, Vue will assign a component name that matches
337
+ the filename of the component.
338
+
339
+ ```bash [Directory structure]
340
+ ├─ components/
341
+ ├─── SomeFolder/
342
+ ├───── MyComponent.vue
343
+ ```
344
+
345
+ In this case, the component name would be `MyComponent`, as far as Vue is concerned. If you wanted to use `<KeepAlive>` with it, or identify it in the Vue DevTools, you would need to use this name.
346
+
347
+ But in order to auto-import it, you would need to use `SomeFolderMyComponent`.
348
+
349
+ With this change, these two values will match, and Vue will generate a component name that matches the Nuxt pattern for component naming.
350
+
351
+ #### Migration Steps
352
+
353
+ Ensure that you use the updated name in any tests which use `findComponent` from `@vue/test-utils` and in any `<KeepAlive>` which depends on the name of your component.
354
+
355
+ Alternatively, for now, you can disable this behaviour with:
356
+
357
+ ```ts twoslash [nuxt.config.ts]
358
+ export default defineNuxtConfig({
359
+ experimental: {
360
+ normalizeComponentNames: false
361
+ }
362
+ })
363
+ ```
364
+
365
+ ### Unhead v2
366
+
367
+ 🚦 **Impact Level**: Minimal
368
+
369
+ #### What Changed
370
+
371
+ [Unhead](https://unhead.unjs.io/), used to generate `<head>` tags, has been updated to version 2. While mostly compatible it includes several breaking changes
372
+ for lower-level APIs.
373
+
374
+ * Removed props: `vmid`, `hid`, `children`, `body`.
375
+ * Promise input no longer supported.
376
+ * Tags are now sorted using Capo.js by default.
377
+
378
+ #### Migration Steps
379
+
380
+ The above changes should have minimal impact on your app.
381
+
382
+ If you have issues you should verify:
383
+
384
+ * You're not using any of the removed props.
385
+
386
+ ```diff
387
+ useHead({
388
+ meta: [{
389
+ name: 'description',
390
+ // meta tags don't need a vmid, or a key
391
+ - vmid: 'description'
392
+ - hid: 'description'
393
+ }]
394
+ })
395
+ ```
396
+
397
+ * If you're using [Template Params](https://unhead.unjs.io/docs/head/guides/plugins/template-params) or [Alias Tag Sorting](https://unhead.unjs.io/docs/head/guides/plugins/alias-sorting), you will need to explicitly opt in to these features now.
398
+
399
+ ```ts
400
+ import { TemplateParamsPlugin, AliasSortingPlugin } from '@unhead/vue/plugins'
401
+
402
+ export default defineNuxtPlugin({
403
+ setup() {
404
+ const unhead = injectHead()
405
+ unhead.use(TemplateParamsPlugin)
406
+ unhead.use(AliasSortingPlugin)
407
+ }
408
+ })
409
+ ```
410
+
411
+ While not required it's recommend to update any imports from `@unhead/vue` to `#imports` or `nuxt/app`.
412
+
413
+ ```diff
414
+ -import { useHead } from '@unhead/vue'
415
+ +import { useHead } from '#imports'
416
+ ```
417
+
418
+ If you still have issues you may revert to the v1 behavior by enabling the `head.legacy` config.
419
+
420
+ ```ts
421
+ export default defineNuxtConfig({
422
+ unhead: {
423
+ legacy: true,
424
+ }
425
+ })
426
+ ```
427
+
428
+ ### New DOM Location for SPA Loading Screen
429
+
430
+ 🚦 **Impact Level**: Minimal
431
+
432
+ #### What Changed
433
+
434
+ When rendering a client-only page (with `ssr: false`), we optionally render a loading screen (from `app/spa-loading-template.html`), within the Nuxt app root:
435
+
436
+ ```html
437
+ <div id="__nuxt">
438
+ <!-- spa loading template -->
439
+ </div>
440
+ ```
441
+
442
+ Now, we default to rendering the template alongside the Nuxt app root:
443
+
444
+ ```html
445
+ <div id="__nuxt"></div>
446
+ <!-- spa loading template -->
447
+ ```
448
+
449
+ #### Reasons for Change
450
+
451
+ This allows the spa loading template to remain in the DOM until the Vue app suspense resolves, preventing a flash of white.
452
+
453
+ #### Migration Steps
454
+
455
+ If you were targeting the spa loading template with CSS or `document.queryElement` you will need to update your selectors. For this purpose you can use the new `app.spaLoaderTag` and `app.spaLoaderAttrs` configuration options.
456
+
457
+ Alternatively, you can revert to the previous behaviour with:
458
+
459
+ ```ts twoslash [nuxt.config.ts]
460
+ export default defineNuxtConfig({
461
+ experimental: {
462
+ spaLoadingTemplateLocation: 'within',
463
+ }
464
+ })
465
+ ```
466
+
467
+ ### Parsed `error.data`
468
+
469
+ 🚦 **Impact Level**: Minimal
470
+
471
+ It was possible to throw an error with a `data` property, but this was not parsed. Now, it is parsed and made available in the `error` object. Although a fix, this is technically a breaking change if you were relying on the previous behavior and parsing it manually.
472
+
473
+ #### Migration Steps
474
+
475
+ Update your custom `error.vue` to remove any additional parsing of `error.data`:
476
+
477
+ ```diff
478
+ <script setup lang="ts">
479
+ import type { NuxtError } from '#app'
480
+
481
+ const props = defineProps({
482
+ error: Object as () => NuxtError
483
+ })
484
+
485
+ - const data = JSON.parse(error.data)
486
+ + const data = error.data
487
+ </script>
488
+ ```
489
+
490
+ Alternatively, you can disable this change:
491
+
492
+ ```ts twoslash [nuxt.config.ts]
493
+ export default defineNuxtConfig({
494
+ experimental: {
495
+ parseErrorData: false
496
+ },
497
+ })
498
+ ```
499
+
500
+ ### More Granular Inline Styles
501
+
502
+ 🚦 **Impact Level**: Moderate
503
+
504
+ Nuxt will now only inline styles for Vue components, not global CSS.
505
+
506
+ #### What Changed
507
+
508
+ Previously, Nuxt would inline all CSS, including global styles, and remove `<link>` elements to separate CSS files. Now, Nuxt will only do this for Vue components (which previously produced separate chunks of CSS). We think this is a better balance of reducing separate network requests (just as before, there will not be separate requests for individual `.css` files per-page or per-component on the initial load), as well as allowing caching of a single global CSS file and reducing the document download size of the initial request.
509
+
510
+ #### Migration Steps
511
+
512
+ This feature is fully configurable and you can revert to the previous behavior by setting `inlineStyles: true` to inline global CSS as well as per-component CSS.
513
+
514
+ ```ts twoslash [nuxt.config.ts]
515
+ export default defineNuxtConfig({
516
+ features: {
517
+ inlineStyles: true
518
+ }
519
+ })
520
+ ```
521
+
522
+ ### Scan Page Meta After Resolution
523
+
524
+ 🚦 **Impact Level**: Minimal
525
+
526
+ #### What Changed
527
+
528
+ We now scan page metadata (defined in `definePageMeta`) _after_ calling the `pages:extend` hook rather than before.
529
+
530
+ #### Reasons for Change
531
+
532
+ This was to allow scanning metadata for pages that users wanted to add in `pages:extend`. We still offer an opportunity to change or override page metadata in a new `pages:resolved` hook.
533
+
534
+ #### Migration Steps
535
+
536
+ If you want to override page metadata, do that in `pages:resolved` rather than in `pages:extend`.
537
+
538
+ ```diff
539
+ export default defineNuxtConfig({
540
+ hooks: {
541
+ - 'pages:extend'(pages) {
542
+ + 'pages:resolved'(pages) {
543
+ const myPage = pages.find(page => page.path === '/')
544
+ myPage.meta ||= {}
545
+ myPage.meta.layout = 'overridden-layout'
546
+ }
547
+ }
548
+ })
549
+ ```
550
+
551
+ Alternatively, you can revert to the previous behaviour with:
552
+
553
+ ```ts twoslash [nuxt.config.ts]
554
+ export default defineNuxtConfig({
555
+ experimental: {
556
+ scanPageMeta: true
557
+ }
558
+ })
559
+ ```
560
+
561
+ ### Shared Prerender Data
562
+
563
+ 🚦 **Impact Level**: Medium
564
+
565
+ #### What Changed
566
+
567
+ We enabled a previously experimental feature to share data from `useAsyncData` and `useFetch` calls, across different pages. See [original PR](https://github.com/nuxt/nuxt/pull/24894).
568
+
569
+ #### Reasons for Change
570
+
571
+ This feature automatically shares payload _data_ between pages that are prerendered. This can result in a significant performance improvement when prerendering sites that use `useAsyncData` or `useFetch` and fetch the same data in different pages.
572
+
573
+ For example, if your site requires a `useFetch` call for every page (for example, to get navigation data for a menu, or site settings from a CMS), this data would only be fetched once when prerendering the first page that uses it, and then cached for use when prerendering other pages.
574
+
575
+ #### Migration Steps
576
+
577
+ Make sure that any unique key of your data is always resolvable to the same data. For example, if you are using `useAsyncData` to fetch data related to a particular page, you should provide a key that uniquely matches that data. (`useFetch` should do this automatically for you.)
578
+
579
+ ```ts [app/pages/test/[slug\\].vue]
580
+ // This would be unsafe in a dynamic page (e.g. `[slug].vue`) because the route slug makes a difference
581
+ // to the data fetched, but Nuxt can't know that because it's not reflected in the key.
582
+ const route = useRoute()
583
+ const { data } = await useAsyncData(async () => {
584
+ return await $fetch(`/api/my-page/${route.params.slug}`)
585
+ })
586
+ // Instead, you should use a key that uniquely identifies the data fetched.
587
+ const { data } = await useAsyncData(route.params.slug, async () => {
588
+ return await $fetch(`/api/my-page/${route.params.slug}`)
589
+ })
590
+ ```
591
+
592
+ Alternatively, you can disable this feature with:
593
+
594
+ ```ts twoslash [nuxt.config.ts]
595
+ export default defineNuxtConfig({
596
+ experimental: {
597
+ sharedPrerenderData: false
598
+ }
599
+ })
600
+ ```
601
+
602
+ ### Default `data` and `error` values in `useAsyncData` and `useFetch`
603
+
604
+ 🚦 **Impact Level**: Minimal
605
+
606
+ #### What Changed
607
+
608
+ `data` and `error` objects returned from `useAsyncData` will now default to `undefined`.
609
+
610
+ #### Reasons for Change
611
+
612
+ Previously `data` was initialized to `null` but reset in `clearNuxtData` to `undefined`. `error` was initialized to `null`. This change is to bring greater consistency.
613
+
614
+ #### Migration Steps
615
+
616
+ If you were checking if `data.value` or `error.value` were `null`, you can update these checks to check for `undefined` instead.
617
+
618
+ ::tip
619
+ You can automate this step by running `npx codemod@latest nuxt/4/default-data-error-value`
620
+ ::
621
+
622
+ If you encounter any issues you can revert back to the previous behavior with:
623
+
624
+ ```ts twoslash [nuxt.config.ts]
625
+ export default defineNuxtConfig({
626
+ experimental: {
627
+ defaults: {
628
+ useAsyncData: {
629
+ value: 'null',
630
+ errorValue: 'null'
631
+ }
632
+ }
633
+ }
634
+ })
635
+ ```
636
+
637
+ Please report an issue if you are doing this, as we do not plan to keep this as configurable.
638
+
639
+ ### Removal of deprecated `boolean` values for `dedupe` option when calling `refresh` in `useAsyncData` and `useFetch`
640
+
641
+ 🚦 **Impact Level**: Minimal
642
+
643
+ #### What Changed
644
+
645
+ Previously it was possible to pass `dedupe: boolean` to `refresh`. These were aliases of `cancel` (`true`) and `defer` (`false`).
646
+
647
+ ```ts twoslash [app.vue]
648
+ const { refresh } = await useAsyncData(async () => ({ message: 'Hello, Nuxt!' }))
649
+
650
+ async function refreshData () {
651
+ await refresh({ dedupe: true })
652
+ }
653
+ ```
654
+
655
+ #### Reasons for Change
656
+
657
+ These aliases were removed, for greater clarity.
658
+
659
+ The issue came up when adding `dedupe` as an option to `useAsyncData`, and we removed the boolean values as they ended up being _opposites_.
660
+
661
+ `refresh({ dedupe: false })` meant 'do not _cancel_ existing requests in favour of this new one'. But passing `dedupe: true` within the options of `useAsyncData` means 'do not make any new requests if there is an existing pending request.' (See [PR](https://github.com/nuxt/nuxt/pull/24564#pullrequestreview-1764584361).)
662
+
663
+ #### Migration Steps
664
+
665
+ The migration should be straightforward:
666
+
667
+ ```diff
668
+ const { refresh } = await useAsyncData(async () => ({ message: 'Hello, Nuxt 3!' }))
669
+
670
+ async function refreshData () {
671
+ - await refresh({ dedupe: true })
672
+ + await refresh({ dedupe: 'cancel' })
673
+
674
+ - await refresh({ dedupe: false })
675
+ + await refresh({ dedupe: 'defer' })
676
+ }
677
+ ```
678
+
679
+ ::tip
680
+ You can automate this step by running `npx codemod@latest nuxt/4/deprecated-dedupe-value`
681
+ ::
682
+
683
+ ### Respect defaults when clearing `data` in `useAsyncData` and `useFetch`
684
+
685
+ 🚦 **Impact Level**: Minimal
686
+
687
+ #### What Changed
688
+
689
+ If you provide a custom `default` value for `useAsyncData`, this will now be used when calling `clear` or `clearNuxtData` and it will be reset to its default value rather than simply unset.
690
+
691
+ #### Reasons for Change
692
+
693
+ 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.
694
+
695
+ #### Migration Steps
696
+
697
+ If you encounter any issues you can revert back to the previous behavior, for now, with:
698
+
699
+ ```ts twoslash [nuxt.config.ts]
700
+ export default defineNuxtConfig({
701
+ experimental: {
702
+ resetAsyncDataToUndefined: true,
703
+ }
704
+ })
705
+ ```
706
+
707
+ Please report an issue if you are doing so, as we do not plan to keep this as configurable.
708
+
709
+ ### Alignment of `pending` value in `useAsyncData` and `useFetch`
710
+
711
+ 🚦 **Impact Level**: Medium
712
+
713
+ The `pending` object returned from `useAsyncData`, `useFetch`, `useLazyAsyncData` and `useLazyFetch` is now a computed property that is `true` only when `status` is also pending.
714
+
715
+ #### What Changed
716
+
717
+ Now, when `immediate: false` is passed, `pending` will be `false` until the first request is made. This is a change from the previous behavior, where `pending` was always `true` until the first request was made.
718
+
719
+ #### Reasons for Change
720
+
721
+ This aligns the meaning of `pending` with the `status` property, which is also `pending` when the request is in progress.
722
+
723
+ #### Migration Steps
724
+
725
+ If you rely on the `pending` property, ensure that your logic accounts for the new behavior where `pending` will only be `true` when the status is also pending.
726
+
727
+ ```diff
728
+ <template>
729
+ - <div v-if="!pending">
730
+ + <div v-if="status === 'success'">
731
+ <p>Data: {{ data }}</p>
732
+ </div>
733
+ <div v-else>
734
+ <p>Loading...</p>
735
+ </div>
736
+ </template>
737
+ <script setup lang="ts">
738
+ const { data, pending, execute, status } = await useAsyncData(() => fetch('/api/data'), {
739
+ immediate: false
740
+ })
741
+ onMounted(() => execute())
742
+ </script>
743
+ ```
744
+
745
+ Alternatively, you can temporarily revert to the previous behavior with:
746
+
747
+ ```ts twoslash [nuxt.config.ts]
748
+ export default defineNuxtConfig({
749
+ experimental: {
750
+ pendingWhenIdle: true
751
+ }
752
+ })
753
+ ```
754
+
755
+ ### Shallow Data Reactivity in `useAsyncData` and `useFetch`
756
+
757
+ 🚦 **Impact Level**: Minimal
758
+
759
+ The `data` object returned from `useAsyncData`, `useFetch`, `useLazyAsyncData` and `useLazyFetch` is now a `shallowRef` rather than a `ref`.
760
+
761
+ #### What Changed
762
+
763
+ When new data is fetched, anything depending on `data` will still be reactive because the entire object is replaced. But if your code changes a property _within_ that data structure, this will not trigger any reactivity in your app.
764
+
765
+ #### Reasons for Change
766
+
767
+ This brings a **significant** performance improvement for deeply nested objects and arrays because Vue does not need to watch every single property/array for modification. In most cases, `data` should also be immutable.
768
+
769
+ #### Migration Steps
770
+
771
+ In most cases, no migration steps are required, but if you rely on the reactivity of the data object then you have two options:
772
+
773
+ 1. You can granularly opt in to deep reactivity on a per-composable basis:
774
+ ```diff
775
+ - const { data } = useFetch('/api/test')
776
+ + const { data } = useFetch('/api/test', { deep: true })
777
+ ```
778
+ 1. You can change the default behavior on a project-wide basis (not recommended):
779
+ ```ts twoslash [nuxt.config.ts]
780
+ export default defineNuxtConfig({
781
+ experimental: {
782
+ defaults: {
783
+ useAsyncData: {
784
+ deep: true
785
+ }
786
+ }
787
+ }
788
+ })
789
+ ```
790
+
791
+ ::tip
792
+ If you need to, you can automate this step by running `npx codemod@latest nuxt/4/shallow-function-reactivity`
793
+ ::
794
+
795
+ ### Absolute Watch Paths in `builder:watch`
796
+
797
+ 🚦 **Impact Level**: Minimal
798
+
799
+ #### What Changed
800
+
801
+ The Nuxt `builder:watch` hook now emits a path which is absolute rather than relative to your project `srcDir`.
802
+
803
+ #### Reasons for Change
804
+
805
+ This allows us to support watching paths which are outside your `srcDir`, and offers better support for layers and other more complex patterns.
806
+
807
+ #### Migration Steps
808
+
809
+ We have already proactively migrated the public Nuxt modules which we are aware use this hook. See [issue #25339](https://github.com/nuxt/nuxt/issues/25339).
810
+
811
+ However, if you are a module author using the `builder:watch` hook and wishing to remain backwards/forwards compatible, you can use the following code to ensure that your code works the same in both Nuxt v3 and Nuxt v4:
812
+
813
+ ```diff
814
+ + import { relative, resolve } from 'node:fs'
815
+ // ...
816
+ nuxt.hook('builder:watch', async (event, path) => {
817
+ + path = relative(nuxt.options.srcDir, resolve(nuxt.options.srcDir, path))
818
+ // ...
819
+ })
820
+ ```
821
+
822
+ ::tip
823
+ You can automate this step by running `npx codemod@latest nuxt/4/absolute-watch-path`
824
+ ::
825
+
826
+ ### Removal of `window.__NUXT__` object
827
+
828
+ #### What Changed
829
+
830
+ We are removing the global `window.__NUXT__` object after the app finishes hydration.
831
+
832
+ #### Reasons for Change
833
+
834
+ This opens the way to multi-app patterns ([#21635](https://github.com/nuxt/nuxt/issues/21635)) and enables us to focus on a single way to access Nuxt app data - `useNuxtApp()`.
835
+
836
+ #### Migration Steps
837
+
838
+ The data is still available, but can be accessed with `useNuxtApp().payload`:
839
+
840
+ ```diff
841
+ - console.log(window.__NUXT__)
842
+ + console.log(useNuxtApp().payload)
843
+ ```
844
+
845
+ ### Directory index scanning
846
+
847
+ 🚦 **Impact Level**: Medium
848
+
849
+ #### What Changed
850
+
851
+ Child folders in your `middleware/` folder are also scanned for `index` files and these are now also registered as middleware in your project.
852
+
853
+ #### Reasons for Change
854
+
855
+ Nuxt scans a number of folders automatically, including `middleware/` and `plugins/`.
856
+
857
+ Child folders in your `plugins/` folder are scanned for `index` files and we wanted to make this behavior consistent between scanned directories.
858
+
859
+ #### Migration Steps
860
+
861
+ Probably no migration is necessary but if you wish to revert to previous behavior you can add a hook to filter out these middleware:
862
+
863
+ ```ts
864
+ export default defineNuxtConfig({
865
+ hooks: {
866
+ 'app:resolve'(app) {
867
+ app.middleware = app.middleware.filter(mw => !/\/index\.[^/]+$/.test(mw.path))
868
+ }
869
+ }
870
+ })
871
+ ```
872
+
873
+ ### Template Compilation Changes
874
+
875
+ 🚦 **Impact Level**: Minimal
876
+
877
+ #### What Changed
878
+
879
+ Previously, Nuxt used `lodash/template` to compile templates located on the file system using the `.ejs` file format/syntax.
880
+
881
+ In addition, we provided some template utilities (`serialize`, `importName`, `importSources`) which could be used for code-generation within these templates, which are now being removed.
882
+
883
+ #### Reasons for Change
884
+
885
+ In Nuxt v3 we moved to a 'virtual' syntax with a `getContents()` function which is much more flexible and performant.
886
+
887
+ In addition, `lodash/template` has had a succession of security issues. These do not really apply to Nuxt projects because it is being used at build-time, not runtime, and by trusted code. However, they still appear in security audits. Moreover, `lodash` is a hefty dependency and is unused by most projects.
888
+
889
+ Finally, providing code serialization functions directly within Nuxt is not ideal. Instead, we maintain projects like [unjs/knitwork](http://github.com/unjs/knitwork) which can be dependencies of your project, and where security issues can be reported/resolved directly without requiring an upgrade of Nuxt itself.
890
+
891
+ #### Migration Steps
892
+
893
+ We have raised PRs to update modules using EJS syntax, but if you need to do this yourself, you have three backwards/forwards-compatible alternatives:
894
+
895
+ * Moving your string interpolation logic directly into `getContents()`.
896
+ * Using a custom function to handle the replacement, such as in https://github.com/nuxt-modules/color-mode/pull/240.
897
+ * Use `es-toolkit/compat` (a drop-in replacement for lodash template), as a dependency of _your_ project rather than Nuxt:
898
+
899
+ ```diff
900
+ + import { readFileSync } from 'node:fs'
901
+ + import { template } from 'es-toolkit/compat'
902
+ // ...
903
+ addTemplate({
904
+ fileName: 'appinsights-vue.js'
905
+ options: { /* some options */ },
906
+ - src: resolver.resolve('./runtime/plugin.ejs'),
907
+ + getContents({ options }) {
908
+ + const contents = readFileSync(resolver.resolve('./runtime/plugin.ejs'), 'utf-8')
909
+ + return template(contents)({ options })
910
+ + },
911
+ })
912
+ ```
913
+
914
+ Finally, if you are using the template utilities (`serialize`, `importName`, `importSources`), you can replace them as follows with utilities from `knitwork`:
915
+
916
+ ```ts
917
+ import { genDynamicImport, genImport, genSafeVariableName } from 'knitwork'
918
+
919
+ const serialize = (data: any) => JSON.stringify(data, null, 2).replace(/"{(.+)}"(?=,?$)/gm, r => JSON.parse(r).replace(/^{(.*)}$/, '$1'))
920
+
921
+ const importSources = (sources: string | string[], { lazy = false } = {}) => {
922
+ return toArray(sources).map((src) => {
923
+ if (lazy) {
924
+ return `const ${genSafeVariableName(src)} = ${genDynamicImport(src, { comment: `webpackChunkName: ${JSON.stringify(src)}` })}`
925
+ }
926
+ return genImport(src, genSafeVariableName(src))
927
+ }).join('\n')
928
+ }
929
+
930
+ const importName = genSafeVariableName
931
+ ```
932
+
933
+ ::tip
934
+ You can automate this step by running `npx codemod@latest nuxt/4/template-compilation-changes`
935
+ ::
936
+
937
+ ### Removal of Experimental Features
938
+
939
+ 🚦 **Impact Level**: Minimal
940
+
941
+ #### What Changed
942
+
943
+ Four experimental features are no longer configurable in Nuxt 4:
944
+
945
+ * `experimental.treeshakeClientOnly` will be `true` (default since v3.0)
946
+ * `experimental.configSchema` will be `true` (default since v3.3)
947
+ * `experimental.polyfillVueUseHead` will be `false` (default since v3.4)
948
+ * `experimental.respectNoSSRHeader` will be `false` (default since v3.4)
949
+ * `vite.devBundler` is no longer configurable - it will use `vite-node` by default
950
+
951
+ #### Reasons for Change
952
+
953
+ These options have been set to their current values for some time and we do not have a reason to believe that they need to remain configurable.
954
+
955
+ #### Migration Steps
956
+
957
+ * `polyfillVueUseHead` is implementable in user-land with [this plugin](https://github.com/nuxt/nuxt/blob/f209158352b09d1986aa320e29ff36353b91c358/packages/nuxt/src/head/runtime/plugins/vueuse-head-polyfill.ts#L10-L11)
958
+
959
+ * `respectNoSSRHeader`is implementable in user-land with [server middleware](https://github.com/nuxt/nuxt/blob/c660b39447f0d5b8790c0826092638d321cd6821/packages/nuxt/src/core/runtime/nitro/no-ssr.ts#L8-L9)
960
+
961
+ ## Nuxt 2 vs. Nuxt 3+
962
+
963
+ In the table below, there is a quick comparison between 3 versions of Nuxt:
964
+
965
+ Feature / Version | Nuxt 2 | Nuxt Bridge | Nuxt 3+
966
+ -------------------------|-----------------|------------------|---------
967
+ Vue | 2 | 2 | 3
968
+ Stability | 😊 Stable | 😊 Stable | 😊 Stable
969
+ Performance | 🏎 Fast | ✈️ Faster | 🚀 Fastest
970
+ Nitro Engine | ❌ | ✅ | ✅
971
+ ESM support | 🌙 Partial | 👍 Better | ✅
972
+ TypeScript | ☑️ Opt-in | 🚧 Partial | ✅
973
+ Composition API | ❌ | 🚧 Partial | ✅
974
+ Options API | ✅ | ✅ | ✅
975
+ Components Auto Import | ✅ | ✅ | ✅
976
+ `<script setup>` syntax | ❌ | 🚧 Partial | ✅
977
+ Auto Imports | ❌ | ✅ | ✅
978
+ webpack | 4 | 4 | 5
979
+ Vite | ⚠️ Partial | 🚧 Partial | ✅
980
+ Nuxi CLI | ❌ Old | ✅ nuxi | ✅ nuxi
981
+ Static sites | ✅ | ✅ | ✅
982
+
983
+ ## Nuxt 2 to Nuxt 3+
984
+
985
+ The migration guide provides a step-by-step comparison of Nuxt 2 features to Nuxt 3+ features and guidance to adapt your current application.
986
+
987
+ ::read-more{to="/docs/migration/overview"}
988
+ Check out the **guide to migrating from Nuxt 2 to Nuxt 3**.
989
+ ::
990
+
991
+ ## Nuxt 2 to Nuxt Bridge
992
+
993
+ If you prefer to progressively migrate your Nuxt 2 application to Nuxt 3, you can use Nuxt Bridge. Nuxt Bridge is a compatibility layer that allows you to use Nuxt 3+ features in Nuxt 2 with an opt-in mechanism.
994
+
995
+ ::read-more{to="/docs/bridge/overview"}
996
+ **Migrate from Nuxt 2 to Nuxt Bridge**
997
+ ::