You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Alongside the release of Nuxt v4.6, today also brings a new major release of the Nuxt CLI: @nuxt/cli v4. It ships as a dependency of nuxt, so you'll get it automatically when you upgrade.
Most of what's new is in nuxt dev:
an interactive terminal UI, showing URLs, startup progress and keyboard shortcuts, with panels to dive into error logs, routes, network requests and more
the dev server gives you more info, such as why it reloaded or restarted, which nuxt.config keys changed, where the time went during a slow start or build, and how long each module took to set up
a CLI-level error channel rendered with my-bad (see below), powering things like automatically reloading when a syntax error in nuxt.config.ts is fixed
a lock file in .nuxt/, which lets a second nuxt dev (say, one started by an agent) take over or defer to the one you started, and powers new nuxt curl and nuxt task commands that talk to the running server
There's also a new nuxt docs "<query>" search, and nuxt preview --takeover can replace a running preview server. And in general nuxt/cli is a lot smaller and starts a lot faster:
v3.37
v4.0
@nuxt/cli install size
13.1 MB
3.5 MB
-73%
@nuxt/cli dependencies
70
31
-56%
nuxt dev: first paint
330 ms
50 ms
6.6x faster
nuxt dev: port bound
338 ms
104 ms
3.2x faster
nuxt dev: memory at rest (Linux)
630 MB
440 MB
-30%
Although this is a major version, none of the changes should be breaking for Nuxt v4 users: we require Node.js v22.21+, v24.11+ or v26+, we drop nuxt init and only support npm create nuxt@latest, and Nuxt 2 and @nuxt/bridge are no longer supported.
The biggest thing about this release is our move towards making Nuxt server-agnostic.
I feel that freedom of choice is very much a fundamental value of the web, and one that unites the whole Nuxt team.
You can use pages/ (with vue-router) or not. You can use Vite, webpack or Rspack to bundle your code. You can pick from dozens of providers to deploy to, pick any image or font provider, choose any database adapter. In every case, the framework is the same.
The server side was different. #app composables imported h3 types, server code imported from h3 and nitropack, and every module that touched the server was tied to whichever major version of those packages Nuxt happened to depend on.
This has become particularly clear as we have been upgrading to new majors of h3 and nitro, which ship breaking changes with a cascading effect throughout the whole ecosystem.
👉 This release changes that.
Alongside explicitly defining our public API in nuxt/kit (which now does not refer to external packages), Nuxt now specifies our own types for the request event, route rules and typed $fetch, and we expose an import surface (nuxt/server) for the server utilities that will be needed by most apps.
This is the culmination of work we started almost a year ago, making it possible to use any server builder with Nuxt, not just Nitro (#33462).
Of course, under the hood, nuxt/server is still powered by Nitro by default - though we are also announcing a second, experimental implementation, @nuxt/vite-server, which allows pure-Vite server builds using the Vite Environment API.
[!IMPORTANT]
We believe that Nitro is still the right choice for almost everyone.
🌟 We see a number of key benefits for nuxt/server.
it smooths the upgrade to Nuxt 5, which moves to Nitro v3 and h3 v2. Server code written against nuxt/server on 4.6 runs unchanged there, so a module can ship one file for both.
it decouples Nuxt from the Nitro release cycle. Because we 'own' the API, we can adapt to breaking changes in Nitro or h3 without requiring a future major - and we can release Nuxt majors without having to wait for upstream releases.
it makes Nuxt's code more maintainable. It helps us preserve the separation of concerns between our API - /app and /server - and the bundler + server that you ultimately want to build your app.
... and there are a number of other benefits too, from a single type surface to being able to iterate more quickly on features.
Finally, I want to say a special thank-you to @pi0, whose relentless focus on server agnosticism and work on h3, Nitro and web-standard server primitives over the last few years is what makes a portable RequestEvent possible at all. Thank you, Pooya. ❤️
Almost every feature in this release is already in the Nuxt 5 branch, and most of the remaining Nuxt 5 defaults can be tested today with future.compatibilityVersion: 5 (more details below!).
[!TIP]
Nuxt 3 reached end-of-life on July 31, 2026, so there is no 3.x release alongside this one. If you're still on v3, the upgrade guide is waiting for you.
👀 Highlights
🤷 If you've read this far I'm afraid I have bad news for you: there's a lot more still to say! Nuxt 4.6 is one of our biggest minor releases, with over 420 commits since v4.5.2.
... so, you might want to grab a coffee! ☕️
🧩 nuxt/server
It has been asked for for a long time, and it now exists (#36275)!
nuxt/server is a new import source for server code: handlers, middleware and utilities - a complement to nuxt/app. Where nuxt/app is for the part of your application that also runs in the browser, nuxt/server is for the part that only runs on the server.
import{defineEventHandler,getQuery}from'nuxt/server'exportdefaultdefineEventHandler((event)=>{const{ name }=getQuery<{name?: string}>(event)return{message: `Hello, ${name??'world'}!`}})
The utilities use web standards and are typed against a portable RequestEvent:
Under @nuxt/nitro-server they are backed by Nitro and h3, but you never import from either.
So the same handler runs under Nitro v2, Nitro v3 or @nuxt/vite-server, and a module that imports from nuxt/server doesn't need a peer dependency on h3 or nitropack.
We think this will make a big difference in smoothing out the upgrade to Nuxt v5 and Nitro v3.
There is a typing benefit too. We no longer hoist h3 or Nitro types into your app to type useRequestEvent, $fetch or route rules, which removes a source of type conflicts when versions differ (#36212, #36214, #36293).
The surface is small, and covers what published modules and user code typically need: defineEventHandler, createError/isNuxtError, request URL, headers, query, body (plain and validated with any Standard Schema library or a function), cookies, redirects, response status, getRouterParam(s), getRequestIP, handleCors, getRouteRules, useRuntimeConfig, useAppConfig and sessions.
We encourage you to use web APIs (event.req.headers, for example), or raise an issue if there's functionality you're missing from nuxt/server 🙏
If you do need to step outside nuxt/server for a particular handler, don't worry! Nothing has been taken away: import defineEventHandler and the helpers you need from h3 or nitropack/runtime as before, and that handler works exactly as it did on Nuxt 4.5.
[!IMPORTANT]
On Nuxt 4, the auto-importeddefineEventHandler, getQuery, readBody and the rest are still h3's own helpers, which take a different shape of event. Import from nuxt/server explicitly, including defineEventHandler to use the new server runtime. If you mix the two, you'll see a NUXT_E8012 error which should tell you which import to change.
A few helpers also behave differently from their h3 v1 namesakes: sendRedirect returns the response rather than sending it, createError takes status and statusText, and response headers are set through event.res.headers. The upgrade guide has a table of the differences.
Nothing in this release requires a migration. But if you have server code you'd like to make portable ahead of Nuxt 5, this is the best way.
Nuxt now has a root application secret: runtimeConfig.appSecret, set with NUXT_APP_SECRET (#35874, thanks to @onmax). Modules and server features derive purpose-specific secrets from it with deriveSecret(purpose), so NUXT_APP_SECRET is the only secret you need to configure.
openssl rand -base64 32
In development Nuxt generates and persists one if none is configured (and warns the first time a derived secret is used). Builds never generate one.
The first thing to use it is a set of session helpers in nuxt/server (#36358). Sessions are sealed into a cookie with iron, so there's no server-side storage to configure:
[!NOTE]
As a reminder, runtime config keys prefixed with app (runtimeConfig.app, runtimeConfig.appSecret) are reserved for Nuxt.
🎯 Typed $fetch, rebuilt
$fetch and useFetch have been typed from your server routes for a long time. But the types were derived from Nitro's InternalApi interface, and past a few hundred routes they hit TypeScript's instantiation limit with the familiar TS2589: Type instantiation is excessively deep and possibly infinite.
We've rebuilt typed fetch on top of fetchdts (#36238). Nuxt compiles your server routes into a route tree with an exact-match table for static paths and accessors specialised to your route set. Resolution cost now scales with call sites, not with route count:
routes
before
after
100
1,397,361 instantiations / 0.77s
51,558 / 0.26s
300
5,774,425 / 3.49s (TS2589)
51,558 / 0.16s
1000
12,831,467 / 7.44s (TS2589)
51,558 / 0.20s
3000 (200 call sites)
71,875,148 / 53.63s (TS2589)
101,678 / 0.62s
Peak memory for the same runs dropped from 946 MB to 140 MB. 🔥
Plus, the route set also carries the body, query and headers a handler validates, so calls are checked more tightly than before:
// server/api/users.post.ts validates { title: string, count: number }await$fetch('/api/users',{method: 'post',body: {title: 'a',count: 1}})await$fetch('/api/users',{method: 'post',body: {title: 'a',count: 'no'}})// ^ not assignable to numberawait$fetch('/api/users',{method: 'post'})// ^ body is required
On Nuxt 4 this is opt-in, because there are small changes to type inference, and hand-written ServerRoutes augmentations need a small rewrite. It is the default in Nuxt 5.
There's also a new experimental.strictRouteTypes option to reject calls to paths that don't exist (otherwise these just return unknown), and an 'isomorphic' mode that types your pages as GET routes too if you want to be able to $fetch from the Vue renderer with type safety.
Server-side errors in development used to look like this:
ReferenceError: foo is not defined
at Object.<anonymous> (/_nuxt/app/pages/index.vue:1:1)
There was no source position or code frame, and the Youch iframe we rendered could not show you the frame in your own source either. Both are now fixed (#36258, nuxt/cli#1518).
Dev SSR stack traces are now mapped before anything reads the error, and the Youch overlay has been replaced with my-bad. It renders into your app's own error page as an overlay (or as a standalone page when the app can't render one), with the mapped stack trace and a code frame from your source, and the same report is printed in your terminal. We are still working with @atinux, @HugoRCD and @antfu to make these pages nicer still.
With Nuxt CLI v4, there is a single live error channel at the CLI level. It survives worker restarts, so (for example) a syntax error in nuxt.config.ts will live-reload the page once you fix it. Each error is rendered once rather than at every layer it passes through, and the same channel streams build progress and app logs to the dev panel.
🎨 A new loading screen, 404 and error pages
@HugoRCD has redrawn the loading screen you see while the dev server starts. It is now a WebGL2 particle field that traces a mountain range behind the Nuxt lockup (#36178), using a single shader and a single draw call. Without WebGL2 it falls back to the static lockup, and with prefers-reduced-motion the animation stops. Try hovering over it. 🏔️
Hugo also gave the built-in 404 and error pages a neutral palette and lighter type (#36255), and @MirkoJa added a back button to the 404 page (#35688).
⚡️ Vue Vapor support
Nuxt now supports Vue 3.6's Vapor Mode in interop mode (#35759). Your app root stays on the virtual DOM, and you can opt individual components or pages into Vapor by adding the vapor attribute to <script setup>:
Routing, useAsyncData, layouts and most built-in components keep working unchanged. Along the way we made the auto-import loader, useAsyncData, definePageMeta and slot inspection Vapor-aware, and we now have a Vapor test suite so we can track what's supported.
[!NOTE]
This requires Vue ^3.6.0-rc.2 or newer. See the known limitations and try it in a fresh project first.
🧩 Addons for useFetch and useAsyncData
@cernymatej has added an addons option to the createUseFetch and createUseAsyncData factories (#35797). An addon can declare custom call-site options, adjust the merged options, wrap the handler with middleware, and extend what the composable returns, and it can be reused across as many custom instances as you like.
This resolves a long list of feature requests for useAsyncData and useFetch (refresh on focus, polling, retries, auth headers and more) without making the core composables opinionated about any of them.
There is a lot of performance work in this release.
<NuxtLink> renders 58% faster on the server (#36015). Internal links are now rendered as a plain <a> with no useLink, no computed and no reactive state. Rendering 200 links went from 1.36ms to 0.57ms, which is 1.4x faster than a bare <RouterLink>.
Early 404s (#36117). With experimental.early404, page routes are compiled into a static matcher at build time and requests that can't match any page skip creating the Vue app, running plugins and middleware entirely. On an app with 30 pages and ~23ms of boot work per render, a JSON 404 went from 37.1ms to 0.3ms.
**Templates decla
[!IMPORTANT]
✂ PR body was truncated to here.
Configuration
📅 Schedule: (UTC)
Branch creation
At any time (no schedule defined)
Automerge
At any time (no schedule defined)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
👻 Immortal: This PR will be recreated if closed unmerged. Get config help if that's undesired.
If you want to rebase/retry this PR, check this box
Error: Failed to import test file .../runner/work/npmx.dev/npmx..../nuxt/composables/use-command-palette-commands.spec.ts
❯ node_modules/.cache.../676879ce89bf73d10ed3edb978c6e79ac1de83ce/deps/plugins.DPQHVT0p-BlQrJL1P.js?v=cdf415d4:5847:5
❯ collectTests node_modules/.cache.../676879ce89bf73d10ed3edb978c6e79ac1de83ce/deps/plugins.DPQHVT0p-BlQrJL1P.js?v=cdf415d4:5823:3
❯ startTests node_modules/.cache.../676879ce89bf73d10ed3edb978c6e79ac1de83ce/deps/plugins.DPQHVT0p-BlQrJL1P.js?v=cdf415d4:6511:17
Caused by: Caused by: SyntaxError: Identifier 'vi' has already been declared
vitest unhandled errors > Unhandled Error: Failed to run the test /home/runner/work/npmx.dev/npmx.dev/test/nuxt/components/Input/Base.spec.ts.
Stack Traces | 0s run time
Error: Failed to run the test .../runner/work/npmx.dev/npmx..../components/Input/Base.spec.ts.
Caused by: Caused by: Error: The iframe ".../runner/work/npmx.dev/npmx..../components/Input/Base.spec.ts" did not become ready within 60000ms. The tester likely failed to initialize, check the browser console for errors.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note
This PR body was truncated due to platform limits.
This PR contains the following updates:
4.1.0→4.3.44.5.2→4.6.34.5.2→4.6.33.5.42→3.5.44Warning
Some dependencies could not be looked up. Check the Dependency Dashboard for more information.
Release Notes
nuxt/test-utils (@nuxt/test-utils)
v4.3.4Compare Source
👉 Changelog
compare changes
🩹 Fixes
setuphooks (#1855)nuxtConfigoverrides to dev server (#1851)h3version from nitro path (#1844)💅 Refactors
package-manager-detectorworkspace + deno handling (1d59009c7)🏡 Chore
❤️ Contributors
v4.3.3Compare Source
👉 Changelog
compare changes
🔥 Performance
toWebHandlerinstead ofnode-mock-http(abe8ad944)h3from the project (b5d802f03)estree-walkerwithoxc-walker(8e8ca5e8b)local-pkgwithexsolve(38938a879)c12with native dotenv parsing (26c72afa0)destrandsculeusage (e4bfd2ebe)node-fetch-nativepolyfill (6b654de5c)nypmwithpackage-manager-detector(#1828)🩹 Fixes
registerEndpointagainst the projecth3(#1831)🏡 Chore
vueto peer dependencies (e70592ffa)nuxtversions as peer dependency (b473892c9)🤖 CI
0ad46c892)pnpm/setupanddevEngines(#1814)❤️ Contributors
v4.3.2Compare Source
👉 Changelog
compare changes
🩹 Fixes
vitest/environmentsimport opaque to rolldown (6d1195d38)❤️ Contributors
v4.3.1Compare Source
👉 Changelog
compare changes
🩹 Fixes
^5as peer dependency (#1809)🏡 Chore
e68ecfa28)🎉 New Contributors
❤️ Contributors
v4.3.0Compare Source
👉 Changelog
compare changes
🚀 Enhancements
unmockNuxtImporthelper (#1795)🩹 Fixes
✅ Tests
🎉 New Contributors
❤️ Contributors
v4.2.0Compare Source
👉 Changelog
compare changes
🚀 Enhancements
🩹 Fixes
mockNuxtImportsourcemap mapped position (#1787)setupTimeoutin playwright + surface server logs (#1785)includeSourcein both projects indefineVitestConfig(#1763)taskkillto kill processes on windows (#1760)🏡 Chore
latestranges with caret ranges (#1773)✅ Tests
🤖 CI
🎉 New Contributors
❤️ Contributors
nuxt/nuxt (@nuxtjs/i18n>@nuxt/kit)
v4.6.3Compare Source
👉 Changelog
compare changes
🚨 Security fixes
63299e13c) - GHSA-x287-wcfc-q46cc7e204ec6) - GHSA-648h-gw55-m2g716dfdc2a9) - GHSA-3xf4-9g3c-4h4w440250d97) - GHSA-9v7c-q64q-vv33b20372dbe) - GHSA-2fxj-c3j7-rr69🔥 Performance
fs.allow(#36702)🩹 Fixes
navigateTowhile middleware runs (#36650)_asyncData(#36709)import.metakeys + limit replace to client dev (#36703)#appcomposables (#36696)shared/in build cache hash (#36704)rootDir(#36697)updateCallbackDoneof aborted view transition (#36639)sensitive: false(#36708)634cba64c)noScriptswildcard patterns single-segment (fa323db7d)nuxt/schemaaugmentation (#36633)nuxt/*imports (#36632)vite.configfiles (#36628)include(#36627)v-forsources in island renders at render time (#36623)📖 Documentation
build:manifesthook (#36695)4b12d15c9)✅ Tests
NUXT_B4023as main-only (4fd54ff9a)453406999)NUXT_B9006as main-only (e107e0f40)🤖 CI
132698a46)7d228ba4b)bad8c3637)9d47884f3)1d89ad434)🎉 New Contributors
❤️ Contributors
v4.6.2Compare Source
👉 Changelog
compare changes
🩹 Fixes
useHeadSaferules to server island head (493565e99)010f6b43d)f6cfee52c)rawVaporSlotKeyfrom vue (#36614)experimental.typescriptPlugin(#36561)AnyFetchPathfor emitted server route types (#36610)nuxt/appto the app's runtime (#36587)appMiddleware(#36565)render:responselisteners (#36583)vueto its bundler build in dev (#36569)/@id/in dev (#36549)nitroAutoImportsis off (#36548)💅 Refactors
📖 Documentation
H(TTP)toHTTP(#36559)proxyroute rule (4a529be68)definePluginanddefineNitroPluginin server plugin tip (64b8e0334)e551df267)7499a445b)🏡 Chore
3110d6947)fb01db4eb)nuxt/appentry shim from root typecheck (c65cef3a8)test:engines(6ce412822)#build/server-routesstub via root tsconfig paths (8235dd605)✅ Tests
ca784deb7)🤖 CI
inlinedDependenciesin autofix (4b6da3a29)917bdf218)🎉 New Contributors
❤️ Contributors
v4.6.1Compare Source
👉 Changelog
compare changes
🔥 Performance
cffb182d6)🩹 Fixes
baseURLwhen fetch response type is named (#36523)nitropack/typesdeclarations under nodenext (242e1971e)falseinsetPageLayouttypes (#36506)Serializeobject types with JSON (#36503)publishConfigconfig export extension (64fa191cb)RequestEventtonuxtdev server handlers (e6beceea2)$fetchwith nuxt'sTypedFetch(#36492).wranglerdirectory (#36487)BroadcastChannelthrows (#36480)nuxtdev server handler variant (452052793)21657c6fc)unheadfrom nuxt in server bundle (dc1e2d6e6)💅 Refactors
package-manager-detectorworkspace + deno handling (1563a6d25)📖 Documentation
nuxt/serverscope (7c36a1352)🏡 Chore
CLAUDE.md(#36462)bdf98e936)787d0d980)✅ Tests
30742865c)1f22a114d)🤖 CI
latest(682605dba)3b8853489)🎉 New Contributors
❤️ Contributors
v4.6.0Compare Source
📣 Some news
🖥️ Nuxt CLI v4
Alongside the release of Nuxt v4.6, today also brings a new major release of the Nuxt CLI:
@nuxt/cliv4. It ships as a dependency ofnuxt, so you'll get it automatically when you upgrade.Most of what's new is in
nuxt dev:nuxt.configkeys changed, where the time went during a slow start or build, and how long each module took to set upmy-bad(see below), powering things like automatically reloading when a syntax error innuxt.config.tsis fixed.nuxt/, which lets a secondnuxt dev(say, one started by an agent) take over or defer to the one you started, and powers newnuxt curlandnuxt taskcommands that talk to the running serverThere's also a new
nuxt docs "<query>"search, andnuxt preview --takeovercan replace a running preview server. And in generalnuxt/cliis a lot smaller and starts a lot faster:@nuxt/cliinstall size@nuxt/clidependenciesnuxt dev: first paintnuxt dev: port boundnuxt dev: memory at rest (Linux)Although this is a major version, none of the changes should be breaking for Nuxt v4 users: we require Node.js v22.21+, v24.11+ or v26+, we drop
nuxt initand only supportnpm create nuxt@latest, and Nuxt 2 and@nuxt/bridgeare no longer supported.👉 Check out the full Nuxt CLI v4 release notes for everything that's changed.
💡 A server-agnostic Nuxt
The biggest thing about this release is our move towards making Nuxt server-agnostic.
I feel that freedom of choice is very much a fundamental value of the web, and one that unites the whole Nuxt team.
You can use
pages/(with vue-router) or not. You can use Vite, webpack or Rspack to bundle your code. You can pick from dozens of providers to deploy to, pick any image or font provider, choose any database adapter. In every case, the framework is the same.The server side was different.
#appcomposables imported h3 types, server code imported fromh3andnitropack, and every module that touched the server was tied to whichever major version of those packages Nuxt happened to depend on.This has become particularly clear as we have been upgrading to new majors of h3 and nitro, which ship breaking changes with a cascading effect throughout the whole ecosystem.
👉 This release changes that.
Alongside explicitly defining our public API in
nuxt/kit(which now does not refer to external packages), Nuxt now specifies our own types for the request event, route rules and typed$fetch, and we expose an import surface (nuxt/server) for the server utilities that will be needed by most apps.This is the culmination of work we started almost a year ago, making it possible to use any server builder with Nuxt, not just Nitro (#33462).
Of course, under the hood,
nuxt/serveris still powered by Nitro by default - though we are also announcing a second, experimental implementation,@nuxt/vite-server, which allows pure-Vite server builds using the Vite Environment API.🌟 We see a number of key benefits for
nuxt/server.nuxt/serveron 4.6 runs unchanged there, so a module can ship one file for both./appand/server- and the bundler + server that you ultimately want to build your app.... and there are a number of other benefits too, from a single type surface to being able to iterate more quickly on features.
Finally, I want to say a special thank-you to @pi0, whose relentless focus on server agnosticism and work on h3, Nitro and web-standard server primitives over the last few years is what makes a portable
RequestEventpossible at all. Thank you, Pooya. ❤️Almost every feature in this release is already in the Nuxt 5 branch, and most of the remaining Nuxt 5 defaults can be tested today with
future.compatibilityVersion: 5(more details below!).👀 Highlights
🤷 If you've read this far I'm afraid I have bad news for you: there's a lot more still to say! Nuxt 4.6 is one of our biggest minor releases, with over 420 commits since v4.5.2.
... so, you might want to grab a coffee! ☕️
🧩
nuxt/serverIt has been asked for for a long time, and it now exists (#36275)!
nuxt/serveris a new import source for server code: handlers, middleware and utilities - a complement tonuxt/app. Wherenuxt/appis for the part of your application that also runs in the browser,nuxt/serveris for the part that only runs on the server.The utilities use web standards and are typed against a portable
RequestEvent:Under
@nuxt/nitro-serverthey are backed by Nitro and h3, but you never import from either.So the same handler runs under Nitro v2, Nitro v3 or
@nuxt/vite-server, and a module that imports fromnuxt/serverdoesn't need a peer dependency onh3ornitropack.We think this will make a big difference in smoothing out the upgrade to Nuxt v5 and Nitro v3.
There is a typing benefit too. We no longer hoist h3 or Nitro types into your app to type
useRequestEvent,$fetchor route rules, which removes a source of type conflicts when versions differ (#36212, #36214, #36293).The surface is small, and covers what published modules and user code typically need:
defineEventHandler,createError/isNuxtError, request URL, headers, query, body (plain and validated with any Standard Schema library or a function), cookies, redirects, response status,getRouterParam(s),getRequestIP,handleCors,getRouteRules,useRuntimeConfig,useAppConfigand sessions.We encourage you to use web APIs (
event.req.headers, for example), or raise an issue if there's functionality you're missing fromnuxt/server🙏If you do need to step outside
nuxt/serverfor a particular handler, don't worry! Nothing has been taken away: importdefineEventHandlerand the helpers you need fromh3ornitropack/runtimeas before, and that handler works exactly as it did on Nuxt 4.5.A few helpers also behave differently from their h3 v1 namesakes:
sendRedirectreturns the response rather than sending it,createErrortakesstatusandstatusText, and response headers are set throughevent.res.headers. The upgrade guide has a table of the differences.Nothing in this release requires a migration. But if you have server code you'd like to make portable ahead of Nuxt 5, this is the best way.
👉 Read the server imports guide.
🔐
appSecretand sessionsNuxt now has a root application secret:
runtimeConfig.appSecret, set withNUXT_APP_SECRET(#35874, thanks to @onmax). Modules and server features derive purpose-specific secrets from it withderiveSecret(purpose), soNUXT_APP_SECRETis the only secret you need to configure.In development Nuxt generates and persists one if none is configured (and warns the first time a derived secret is used). Builds never generate one.
The first thing to use it is a set of session helpers in
nuxt/server(#36358). Sessions are sealed into a cookie with iron, so there's no server-side storage to configure:🎯 Typed
$fetch, rebuilt$fetchanduseFetchhave been typed from your server routes for a long time. But the types were derived from Nitro'sInternalApiinterface, and past a few hundred routes they hit TypeScript's instantiation limit with the familiarTS2589: Type instantiation is excessively deep and possibly infinite.We've rebuilt typed fetch on top of
fetchdts(#36238). Nuxt compiles your server routes into a route tree with an exact-match table for static paths and accessors specialised to your route set. Resolution cost now scales with call sites, not with route count:TS2589)TS2589)TS2589)Peak memory for the same runs dropped from 946 MB to 140 MB. 🔥
Plus, the route set also carries the
body,queryandheadersa handler validates, so calls are checked more tightly than before:On Nuxt 4 this is opt-in, because there are small changes to type inference, and hand-written
ServerRoutesaugmentations need a small rewrite. It is the default in Nuxt 5.There's also a new
experimental.strictRouteTypesoption to reject calls to paths that don't exist (otherwise these just returnunknown), and an'isomorphic'mode that types your pages asGETroutes too if you want to be able to$fetchfrom the Vue renderer with type safety.👉 Read more in the experimental features docs.
🐛 Better errors in development with
my-badServer-side errors in development used to look like this:
There was no source position or code frame, and the Youch iframe we rendered could not show you the frame in your own source either. Both are now fixed (#36258, nuxt/cli#1518).
Dev SSR stack traces are now mapped before anything reads the error, and the Youch overlay has been replaced with
my-bad. It renders into your app's own error page as an overlay (or as a standalone page when the app can't render one), with the mapped stack trace and a code frame from your source, and the same report is printed in your terminal. We are still working with @atinux, @HugoRCD and @antfu to make these pages nicer still.With Nuxt CLI v4, there is a single live error channel at the CLI level. It survives worker restarts, so (for example) a syntax error in
nuxt.config.tswill live-reload the page once you fix it. Each error is rendered once rather than at every layer it passes through, and the same channel streams build progress and app logs to the dev panel.🎨 A new loading screen, 404 and error pages
@HugoRCD has redrawn the loading screen you see while the dev server starts. It is now a WebGL2 particle field that traces a mountain range behind the Nuxt lockup (#36178), using a single shader and a single draw call. Without WebGL2 it falls back to the static lockup, and with
prefers-reduced-motionthe animation stops. Try hovering over it. 🏔️Hugo also gave the built-in 404 and error pages a neutral palette and lighter type (#36255), and @MirkoJa added a back button to the 404 page (#35688).
⚡️ Vue Vapor support
Nuxt now supports Vue 3.6's Vapor Mode in interop mode (#35759). Your app root stays on the virtual DOM, and you can opt individual components or pages into Vapor by adding the
vaporattribute to<script setup>:Routing,
useAsyncData, layouts and most built-in components keep working unchanged. Along the way we made the auto-import loader,useAsyncData,definePageMetaand slot inspection Vapor-aware, and we now have a Vapor test suite so we can track what's supported.🧩 Addons for
useFetchanduseAsyncData@cernymatej has added an
addonsoption to thecreateUseFetchandcreateUseAsyncDatafactories (#35797). An addon can declare custom call-site options, adjust the merged options, wrap the handler with middleware, and extend what the composable returns, and it can be reused across as many custom instances as you like.This resolves a long list of feature requests for
useAsyncDataanduseFetch(refresh on focus, polling, retries, auth headers and more) without making the core composables opinionated about any of them.👉 Read about
defineUseFetchAddonanddefineUseAsyncDataAddon.🚀 Performance
There is a lot of performance work in this release.
<NuxtLink>renders 58% faster on the server (#36015). Internal links are now rendered as a plain<a>with nouseLink, no computed and no reactive state. Rendering 200 links went from 1.36ms to 0.57ms, which is 1.4x faster than a bare<RouterLink>.experimental.early404, page routes are compiled into a static matcher at build time and requests that can't match any page skip creating the Vue app, running plugins and middleware entirely. On an app with 30 pages and ~23ms of boot work per render, a JSON 404 went from 37.1ms to 0.3ms.Configuration
📅 Schedule: (UTC)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
👻 Immortal: This PR will be recreated if closed unmerged. Get config help if that's undesired.
This PR was generated by Mend Renovate. View the repository job log.