28Although 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.
|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+, ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
=help: Create new projects with npm create nuxt@latest instead of nuxt init.
breaking[2]:This release requires newer Node.js versions than before.
28Although 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.
|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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
=help: The notes give no migration path. Check that your project does not depend on Nuxt 2 or @nuxt/bridge before upgrading.
Kernpunkte
note[1]:nuxt/server is a new import source for server handlers, middleware and utilities, complementing nuxt/app.release-notes:80
note[2]:$fetch and useFetch are rebuilt on fetchdts, which keeps type checking fast on large route sets and avoids TS2589 errors.release-notes:172
note[3]:Development server errors are shown with my-bad, with mapped stack traces and source code frames.release-notes:219
note[4]:Vue 3.6 Vapor Mode is supported in interop mode, and can be enabled per component or page.release-notes:243
release-notesgithub.com/nuxt/nuxt · 935 Zeilen
> v4.6.0 is the next minor release.
## 📣 Some news
### 🖥️ Nuxt CLI v4
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:
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.
👉 **Check out the full [Nuxt CLI v4 release notes](https://github.com/nuxt/cli/releases/tag/v4.0.0) 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. `#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](https://github.com/nuxt/nuxt/pull/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](https://vite.dev/guide/api-environment#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`.
1. **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.
2. **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.
3. **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](https://github.com/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](https://nuxt.com/docs/getting-started/upgrade) 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](https://github.com/nuxt/nuxt/pull/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.
```ts [server/api/hello.ts]
import { defineEventHandler, getQuery } from 'nuxt/server'
export default defineEventHandler((event) => {
const { name } = getQuery<{ name?: string }>(event)
return { message: `Hello, ${name ?? 'world'}!` }
})
```
The utilities use web standards and are typed against a portable `RequestEvent`:
```ts
event.req // Request
event.url // URL
event.res // { status, statusText, headers }
event.res.headers // Headers
event.context // per-request context
```
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](https://github.com/nuxt/nuxt/pull/36212), [#36214](https://github.com/nuxt/nuxt/pull/36214), [#36293](https://github.com/nuxt/nuxt/pull/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](https://standardschema.dev) library or a function), cookies, redirects, response status, `getRouterParam(s)`, `getRequestIP`, `handleCors`, `getRouteRules`, `useRuntimeConfig`, `useAppConfig` and sessions.
```ts [server/api/users.post.ts]
import { defineEventHandler, readValidatedBody } from 'nuxt/server'
const user = await readValidatedBody(event, z.object({ name: z.string() }))
return { created: user.name }
})
```
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.
```ts [server/api/upload.post.ts]
import { defineEventHandler, readMultipartFormData } from 'h3'
> On Nuxt 4, the _auto-imported_ `defineEventHandler`, `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](https://nuxt.com/docs/getting-started/upgrade#moving-to-nuxtserver).
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](https://nuxt.com/docs/guide/going-further/server-imports).
### 🔐 `appSecret` and sessions
Nuxt now has a root application secret: `runtimeConfig.appSecret`, set with `NUXT_APP_SECRET` ([#35874](https://github.com/nuxt/nuxt/pull/35874), thanks to [@onmax](https://github.com/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.
```bash
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](https://github.com/nuxt/nuxt/pull/36358)). Sessions are sealed into a cookie with [iron](https://github.com/brc-dd/iron-webcrypto), so there's no server-side storage to configure:
```ts [server/api/visits.ts]
import { defineEventHandler, useSession } from 'nuxt/server'
> 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`](https://github.com/danielroe/fetchdts) ([#36238](https://github.com/nuxt/nuxt/pull/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:
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.
```ts [nuxt.config.ts]
export default defineNuxtConfig({
experimental: {
routeTypedFetch: true,
},
})
```
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.
👉 Read more in the [experimental features docs](https://nuxt.com/docs/guide/going-further/experimental-features#routetypedfetch).
### 🐛 Better errors in development with `my-bad`
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](https://github.com/nuxt/nuxt/pull/36258), [nuxt/cli#1518](https://github.com/nuxt/cli/pull/1518)).
Dev SSR stack traces are now mapped before anything reads the error, and the Youch overlay has been replaced with [`my-bad`](https://github.com/danielroe/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](https://github.com/atinux), [@HugoRCD](https://github.com/HugoRCD) and [@antfu](https://github.com/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.
<!-- TODO: record video of fixing a syntax error in nuxt.config.ts and watch the error page live-reload -->
### 🎨 A new loading screen, 404 and error pages
[@HugoRCD](https://github.com/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](https://github.com/nuxt/nuxt/pull/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](https://github.com/nuxt/nuxt/pull/36255)), and [@MirkoJa](https://github.com/MirkoJa) added a back button to the 404 page ([#35688](https://github.com/nuxt/nuxt/pull/35688)).
<!-- SCREENSHOT: release-assets/mac/404-page-{dark,light}.png and release-assets/error-page-prod-{dark,light}.png -->
### ⚡️ Vue Vapor support
Nuxt now supports Vue 3.6's [Vapor Mode](https://github.com/vuejs/core/releases/tag/v3.6.0-rc.1#about-vapor-mode) in interop mode ([#35759](https://github.com/nuxt/nuxt/pull/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>`:
```ts [nuxt.config.ts]
export default defineNuxtConfig({
vue: {
vapor: true,
},
})
```
```vue [app/pages/index.vue]
<script setup vapor lang="ts">
const count = ref(0)
</script>
<template>
<button @click="count++">
count is {{ count }}
</button>
</template>
```
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](https://nuxt.com/docs/guide/concepts/vuejs-development#known-limitations) and try it in a fresh project first.
### 🧩 Addons for `useFetch` and `useAsyncData`
[@cernymatej](https://github.com/cernymatej) has added an `addons` option to the `createUseFetch` and `createUseAsyncData` factories ([#35797](https://github.com/nuxt/nuxt/pull/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.
👉 Read about [`defineUseFetchAddon`](https://nuxt.com/docs/api/utils/define-use-fetch-addon) and [`defineUseAsyncDataAddon`](https://nuxt.com/docs/api/utils/define-use-async-data-addon).
### 🚀 Performance
There is a lot of performance work in this release.
- **`<NuxtLink>` renders 58% faster on the server** ([#36015](https://github.com/nuxt/nuxt/pull/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](https://github.com/nuxt/nuxt/pull/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 declare their dependencies** ([#35875](https://github.com/nuxt/nuxt/pull/35875)). The virtual file templates used in Nuxt's build used to regenerate on every file change. They now declare what invalidates them, so editing a component regenerates 0 of 49 core templates instead of all of them, and the `builder:watch` hook settles in 0.6ms instead of 36ms.
- **Cookie jars** ([#36114](https://github.com/nuxt/nuxt/pull/36114)). `useCookie` parses the cookie header once per request (or once per microtask on the client) instead of on every call. A nice side effect: a cookie set in a plugin during SSR can now be read by a later `useCookie` in a page.
- **`ssr: false` pages are tree-shaken from the server bundle** ([#35836](https://github.com/nuxt/nuxt/pull/35836), thanks to [@Austin1serb](https://github.com/Austin1serb)).
- **Serializable `definePageMeta` keys are extracted at build time** ([#35919](https://github.com/nuxt/nuxt/pull/35919)), behind `experimental.extractSerializablePageMeta`.
- **Faster dev server warm-up**: Vite's server module graph is warmed before the client ([#36154](https://github.com/nuxt/nuxt/pull/36154)), the client graph is crawled during warm-up ([#35870](https://github.com/nuxt/nuxt/pull/35870)), and warm-up yields to your first navigation so it never competes with a real request ([#36412](https://github.com/nuxt/nuxt/pull/36412)).
- **Smaller client bundle**: `unctx` is no longer shipped to the browser and `defu` is skipped for a single `app.config` ([#36371](https://github.com/nuxt/nuxt/pull/36371)).
- **Fewer dependencies**: `@nuxt/kit` no longer depends on `c12`, `untyped`, `confbox`, `pkg-types`, `ufo` or `mlly`, and `jiti` and `giget` are now optional peers loaded only when needed ([#35936](https://github.com/nuxt/nuxt/pull/35936), [#35943](https://github.com/nuxt/nuxt/pull/35943), [#35946](https://github.com/nuxt/nuxt/pull/35946), [#36071](https://github.com/nuxt/nuxt/pull/36071), [#36073](https://github.com/nuxt/nuxt/pull/36073), [#36083](https://github.com/nuxt/nuxt/pull/36083)).
- Lazy impound tracing for builds ([#36172](https://github.com/nuxt/nuxt/pull/36172)), virtual module hooks filtered by id ([#36077](https://github.com/nuxt/nuxt/pull/36077)), `isVue` checks migrated to plugin filters ([#36116](https://github.com/nuxt/nuxt/pull/36116)) and an `flru` prerender cache ([#36340](https://github.com/nuxt/nuxt/pull/36340)).
Put together, on our benchmark machine (arm64 Linux, Node 24.15, medians of 5 runs):
| `nuxt build`, starter | 4.4 s | 3.6 s | **-18%** |
| `nuxt build`, 200 pages / 200 components / 50 routes | 11.6 s | 10.3 s | **-11%** |
| SSR throughput, page with 300 `<NuxtLink>`s | 110 req/s | 140 req/s | **+26%** |
Dev server start-up (spawn to first HTML) on the same machine is about 12% faster on a starter app, with the second request served in roughly half the time, though the CLI major changed alongside so not all of that is Nuxt.
### 📦 Lighter payloads
`useAsyncData` and `useFetch` accept a `serialize: false` option to keep data out of the `__NUXT_DATA__` payload ([#35779](https://github.com/nuxt/nuxt/pull/35779)). That's most useful inside components that never hydrate, and `experimental.stripNeverHydratedData` applies it automatically to data fetched within `hydrate-never` component trees.
Nuxt also warns in development when a page's payload exceeds 100 kB ([#35777](https://github.com/nuxt/nuxt/pull/35777)) and when a route rendered with `noScripts` relies on client-side JavaScript ([#35780](https://github.com/nuxt/nuxt/pull/35780)), and `noScripts` pages keep their non-script resource hints and attach their styles correctly ([#35803](https://github.com/nuxt/nuxt/pull/35803), [#36356](https://github.com/nuxt/nuxt/pull/36356), [#36359](https://github.com/nuxt/nuxt/pull/36359)).
`<NuxtLink>` now prefetches server-page islands ([#35808](https://github.com/nuxt/nuxt/pull/35808)), and [@atinux](https://github.com/atinux) made prefetch hints throttled and prioritised so a page full of links doesn't flood the network ([#36261](https://github.com/nuxt/nuxt/pull/36261), [#36324](https://github.com/nuxt/nuxt/pull/36324)). Building on that, route chunks, layouts, middleware, payloads, islands and resource hints all now go through one client prefetch scheduler with per-kind concurrency caps, deduplication by key, and cancellation of in-flight work when you navigate away ([#36391](https://github.com/nuxt/nuxt/pull/36391)).
### 🔮 Nuxt 5 features, today
Most of what's new in Nuxt 5 is already in 4.6, either as the default or behind a flag. `future.compatibilityVersion: 5` turns on the Nuxt 5 defaults in one go, and every one of them can be enabled (or disabled) individually.
- **Typed `$fetch`** (`experimental.routeTypedFetch`), described above
- **Case-sensitive routing**, matching Nitro ([#35650](https://github.com/nuxt/nuxt/pull/35650), thanks to [@Mateleo](https://github.com/Mateleo))
- **Early return from `navigateTo`** (`experimental.navigateToEarlyReturn`): `navigateTo` in `<script setup>` short-circuits the rest of the setup, so redirects and 404s don't throw on missing data ([#36115](https://github.com/nuxt/nuxt/pull/36115))
- **Serializable page meta extraction** (`experimental.extractSerializablePageMeta`)
- **Normalized page names**, `clearNuxtState` resetting to defaults, `experimental.watcher: 'builder'`, and no auto-imported server-only head composables
- **No `baseUrl` in generated tsconfigs** ([#36040](https://github.com/nuxt/nuxt/pull/36040), thanks to [@oritwoen](https://github.com/oritwoen))
- **Inline error rendering** (`experimental.inlineErrorRendering`): when a server render fails, `error.vue` is rendered in the same request with a plain try/catch, instead of re-entering the server over an internal request to `/__nuxt_error`. Headers and cookies the failed render had already set are kept, error renders no longer pass through Nitro middleware and route rules a second time, and `render:html` fires with the original event ([#36399](https://github.com/nuxt/nuxt/pull/36399)).
The upgrade guide now lists [exactly what the flag changes on Nuxt 4](https://nuxt.com/docs/getting-started/upgrade#testing-nuxt-5).
### 🧪 Experimental: `@nuxt/vite-server`
Since v4.2 `server.builder` has been configurable. This release adds a second server builder: `@nuxt/vite-server`. It builds a Nuxt app with Vite alone ([#36218](https://github.com/nuxt/nuxt/pull/36218), [#36279](https://github.com/nuxt/nuxt/pull/36279), [#36288](https://github.com/nuxt/nuxt/pull/36288)).
This is all you need to do to configure it.
```ts [nuxt.config.ts]
export default defineNuxtConfig({
server: {
builder: 'vite', // 'nitro' is the default
},
})
```
It can produce a pure client SPA, a server-rendered app with a small Node entry, a web-standard `fetch` handler for platforms that provide the server (there are e2e examples for [Cloudflare Workers](https://developers.cloudflare.com/workers/vite-plugin/), [Netlify](https://github.com/netlify/framework-adapters/tree/main/packages/vite-plugin) and [universal deploy](https://github.com/universal-deploy/universal-deploy)), as well as fully static output with `nuxt generate`.
Right now, this helps keep Nuxt's code agnostic, enforce the contract behind `nuxt/server`, and to give Vite plugins that provide a deploy target something to build on. It does not have Nitro's full feature set (there is no storage, caching, tasks or server plugins), and we expect most apps to keep using Nitro. **Nitro remains the default.**
> [!WARNING]
> This is highly experimental and the API will change. `'nitro'` and `'vite'` are new shorthands for `@nuxt/nitro-server` and `@nuxt/vite-server`.
### 🛠️ Developer experience
- **Clickable file paths** in terminal output ([#35898](https://github.com/nuxt/nuxt/pull/35898)). Warnings that mention a file now link to it in your editor, at the line responsible where we know it.
- **IDE hover docs for built-in components** ([#35701](https://github.com/nuxt/nuxt/pull/35701)). Hovering a built-in component like `<NuxtLayout>` or `<NuxtLink>` in a template now shows a short description and a link to the docs. Thanks to [@Ibochkarev](https://github.com/Ibochkarev).
- **`app/types/` and `server/types/`** are included in the right tsconfig, so ambient types and augmentations placed there are picked up ([#35783](https://github.com/nuxt/nuxt/pull/35783), thanks to [@Flo0806](https://github.com/Flo0806), who also added an augmentable `NuxtPageMeta` for typing `NuxtPage.meta` in [#34816](https://github.com/nuxt/nuxt/pull/34816)).
- **Grouping folders in `components/`**: a `(group)/` folder is excluded from the component name ([#35699](https://github.com/nuxt/nuxt/pull/35699), thanks to [@abaza738](https://github.com/abaza738)).
- **Dynamic `expires` in `useCookie`**, accepting a function ([#35628](https://github.com/nuxt/nuxt/pull/35628), thanks to [@DarlanPrado](https://github.com/DarlanPrado)).
- **A warning when a `public/` file shadows an application route** ([#35674](https://github.com/nuxt/nuxt/pull/35674), thanks to [@Norbiros](https://github.com/Norbiros)).
- **Install hints use your package manager** ([#35871](https://github.com/nuxt/nuxt/pull/35871)).
- **Prerender your error pages** as real HTML (`404.html`, or any status codes you pass) with `experimental.prerenderErrorPages` instead of an empty SPA shell ([#35193](https://github.com/nuxt/nuxt/pull/35193), thanks again to [@Flo0806](https://github.com/Flo0806)).
- `$Fetch` is exported from `nuxt/app` ([#35625](https://github.com/nuxt/nuxt/pull/35625)) and `ShallowRef` is in the Vue auto-import preset ([#36266](https://github.com/nuxt/nuxt/pull/36266)), both from [@DamianGlowala](https://github.com/DamianGlowala); `preloadComponents` and the `NuxtIsland` `name` prop are typed ([#35775](https://github.com/nuxt/nuxt/pull/35775)), and `useLayout`, `useLoadingIndicator` and `useRequestHeader` are exported from `nuxt/app` ([#36033](https://github.com/nuxt/nuxt/pull/36033)).
- **`typescript.tsConfig` is now a shared baseline** for all four generated tsconfigs, with `appTsConfig` and `serverTsConfig` for per-context overrides ([#35697](https://github.com/nuxt/nuxt/pull/35697), thanks to [@chairulakmal](https://github.com/chairulakmal)).
- **Top-level `prerender` option**, an alias for `nitro.prerender` in the same way `runtimeConfig` and `routeRules` are top-level ([#32356](https://github.com/nuxt/nuxt/pull/32356)). Nuxt now also points you towards top-level options where they exist, since those work across server builders ([#36416](https://github.com/nuxt/nuxt/pull/36416)).
- **Links to `public/` files just work.** If the router has no route for a `<NuxtLink>` target (a PDF in `public/`, say, or a link from Markdown with Nuxt Content), Nuxt falls through to a full-page load instead of rendering your 404, so `external` is no longer required ([#36169](https://github.com/nuxt/nuxt/pull/36169)).
- **Renaming a component in development refreshes its imports**, so you no longer see stale references to the old file ([#36165](https://github.com/nuxt/nuxt/pull/36165), thanks to [@oritwoen](https://github.com/oritwoen)).
- **Layers**: dependencies of layers installed from `node_modules` are pre-bundled by Vite ([#36208](https://github.com/nuxt/nuxt/pull/36208)), and symlinked layer directories resolve to their real path ([#36402](https://github.com/nuxt/nuxt/pull/36402), thanks to [@silverbackdan](https://github.com/silverbackdan)).
### 🧰 For module authors
If you maintain a module with server code, we've enabled making modules compatible with both Nuxt 4 & 5, without requiring a major bump. (And we'll be opening PRs proactively after the release of Nuxt v4.6 to assist with preparing for a Nuxt v5 release...)
- **One module for Nuxt 4 and Nuxt 5.** `addServerHandler`, `addDevServerHandler` and `addNitroPlugin` accept a map of variants per server API ([#36317](https://github.com/nuxt/nuxt/pull/36317)). Nuxt chooses the most appropriate one. A handler that imports only from `nuxt/server` needs no Nitro 2/3 variants at all (but does require Nuxt v4.6+).
Nuxt reads the file's imports to decide which API it uses. Where that isn't enough, you can declare `meta.compatibility.server`. `getNitroVersion` and `hasNitroVersion` are also there in case you have logic that _explicitly_ requires you to know the Nitro version installed ([#36127](https://github.com/nuxt/nuxt/pull/36127)).
👉 Read the [server compatibility guide](https://nuxt.com/docs/guide/modules/server-compatibility) for more information.
- **Nuxt-owned, augmentable server types**: `ServerTypes`, `ServerRoutes`, `AppRouteRules` and `NuxtRequestContext`. Augment `@nuxt/schema` once; `nuxt/schema` mirrors it ([#36293](https://github.com/nuxt/nuxt/pull/36293)).
- **`useTerminal`** for host-aware prompts, status messages and tasks in progress, rendered by the CLI's dev panel when there is one ([#36162](https://github.com/nuxt/nuxt/pull/36162)).
- **`module:before` and `module:done` hooks**, which Nuxt CLI v4 uses to show per-module setup time as it happens ([#36173](https://github.com/nuxt/nuxt/pull/36173)).
- **`onConfigResolved` and `diffNuxtConfig`** to see what changed between two config loads ([#35853](https://github.com/nuxt/nuxt/pull/35853)).
- **`ensureDependencyInstalled` and `getAddDependencyCommand`** to check for and offer to install optional dependencies with the user's package manager ([#34554](https://github.com/nuxt/nuxt/pull/34554)).
- **Template `dependencies`** to say exactly what should invalidate a template ([#35875](https://github.com/nuxt/nuxt/pull/35875)).
- **`addServerImports`, `addServerImportsDir` and `addServerTemplate`** work the same across Nitro versions; a server tsconfig and versioned route config types are generated for you ([#36265](https://github.com/nuxt/nuxt/pull/36265)).
- **`@nuxt/kit` has an explicit public API** ([#36074](https://github.com/nuxt/nuxt/pull/36074)) and `@nuxt/schema` is an optional peer ([#36246](https://github.com/nuxt/nuxt/pull/36246)).
- Modules can now set `experimental.asyncContext` ([#36175](https://github.com/nuxt/nuxt/pull/36175), thanks to [@cernymatej](https://github.com/cernymatej)), Vite plugins added via kit land at the top level rather than wrapped ([#36037](https://github.com/nuxt/nuxt/pull/36037)), and more build-time warnings have moved to [diagnostic codes](https://nuxt.com/docs/errors) with docs pages ([#36138](https://github.com/nuxt/nuxt/pull/36138)).
- `@nuxt/kit` peer dependency ranges are widened to the versions actually required rather than tracking the latest of each package ([#36417](https://github.com/nuxt/nuxt/pull/36417)), and `updateRuntimeConfig` no longer warns when called before Nitro exists ([#36403](https://github.com/nuxt/nuxt/pull/36403), thanks to [@Neekoras](https://github.com/Neekoras)).
### 🔒 Security
- The internal error route is only served to an error render, and can no longer be reached from outside ([259058cf4](https://github.com/nuxt/nuxt/commit/259058cf4), [#36305](https://github.com/nuxt/nuxt/pull/36305)).
- Dev error reports are scoped for remote peers, and the dev error channel is kept out of production builds ([#36389](https://github.com/nuxt/nuxt/pull/36389), [#36407](https://github.com/nuxt/nuxt/pull/36407)).
- Unhandled error data is no longer passed to the error page ([38a40cfc0](https://github.com/nuxt/nuxt/commit/38a40cfc0)), oversized island bodies are drained before being rejected ([#36320](https://github.com/nuxt/nuxt/pull/36320)), and error-render recursion is tracked in request context rather than via a client-controllable header ([d8c729435](https://github.com/nuxt/nuxt/commit/d8c729435)).
### 🩹 Important fixes
- Awaited `useAsyncData` no longer resolves with unfetched data on hydration ([#36124](https://github.com/nuxt/nuxt/pull/36124)), awaited lazy async data resolves immediately ([#36301](https://github.com/nuxt/nuxt/pull/36301)), and `useAsyncData` types resolve for generic type params ([#36316](https://github.com/nuxt/nuxt/pull/36316)).
- `useRequestFetch` forwards request headers ([#36180](https://github.com/nuxt/nuxt/pull/36180), thanks to [@hdwebpros](https://github.com/hdwebpros)).
- `navigateTo` matches vue-router's path encoding ([#36055](https://github.com/nuxt/nuxt/pull/36055)), preserves percent-encoding in server redirects ([#36112](https://github.com/nuxt/nuxt/pull/36112)), and applies `baseURL` with `open` ([#36197](https://github.com/nuxt/nuxt/pull/36197)).
- Scoped styles apply to server component slots ([#36047](https://github.com/nuxt/nuxt/pull/36047)), island asset requests are deduped ([#36048](https://github.com/nuxt/nuxt/pull/36048)), lazy hydration no longer produces blank remounts ([#36051](https://github.com/nuxt/nuxt/pull/36051)), all from [@oritwoen](https://github.com/oritwoen), and hydrated `nuxt-client` markup is stripped from cached island HTML ([#36298](https://github.com/nuxt/nuxt/pull/36298)).
- A batch of CSS fixes across Vite, webpack and rspack: duplicate CSS links for inlined chunks ([#36056](https://github.com/nuxt/nuxt/pull/36056)), `baseURL` and `url()` rewriting in inlined styles ([#36137](https://github.com/nuxt/nuxt/pull/36137), [#36143](https://github.com/nuxt/nuxt/pull/36143)), island descendant CSS extraction ([#36260](https://github.com/nuxt/nuxt/pull/36260)) and stable style chunk names ([#36361](https://github.com/nuxt/nuxt/pull/36361)).
- View transitions handle interrupted and skipped navigations ([#35537](https://github.com/nuxt/nuxt/pull/35537) from [@Askerka00](https://github.com/Askerka00), [#36314](https://github.com/nuxt/nuxt/pull/36314)), and page transitions stay mounted for nested routes with no children ([#36304](https://github.com/nuxt/nuxt/pull/36304)).
- The streaming shell head renders after the first render pass ([#36120](https://github.com/nuxt/nuxt/pull/36120)).
- Payload URLs are parsed as origin-relative paths, so a path like `//x/_payload.json` no longer serves the wrong payload ([#36409](https://github.com/nuxt/nuxt/pull/36409), spotted by [@Kushalkhemka](https://github.com/Kushalkhemka)).
- `/index.html` is prerendered for client-only apps with islands ([#36299](https://github.com/nuxt/nuxt/pull/36299)).
- `definePageMeta` works at the top level of `setup()` ([#36245](https://github.com/nuxt/nuxt/pull/36245)), and changed auto-import sources are rescanned before their consumers ([#33671](https://github.com/nuxt/nuxt/pull/33671), thanks to [@Flo0806](https://github.com/Flo0806)).
- A template that never finishes compiling now fails the build instead of exiting 0 ([#36322](https://github.com/nuxt/nuxt/pull/36322)).
- Error causes are preserved in development ([#35632](https://github.com/nuxt/nuxt/pull/35632), thanks to [@onmax](https://github.com/onmax)).
- Layout `onBeforeLeave` reuses the pending transition promise ([#36395](https://github.com/nuxt/nuxt/pull/36395)), and `baseURL` and middleware flags are respected in apps without `pages/` ([#36034](https://github.com/nuxt/nuxt/pull/36034)).
- `stripNeverHydratedData` no longer mutates the options object ([#36035](https://github.com/nuxt/nuxt/pull/36035)), the dev error module is stubbed out of production builds ([55e61d75d](https://github.com/nuxt/nuxt/commit/55e61d75d)), and preloading a component that is not global now warns ([47de80ece](https://github.com/nuxt/nuxt/commit/47de80ece)).
### ✅ Upgrading
Our recommendation for upgrading is to run:
```sh
npx nuxt upgrade --dedupe
```
This will refresh your lockfile and pull in all the latest dependencies that Nuxt relies on, including Nuxt CLI v4.
> [!NOTE]
> This release requires Node.js `^22.22.3 || ^24.15.0 || >=26.0.0`.
If you have server code you'd like to make portable, read [Moving to `nuxt/server`](https://nuxt.com/docs/getting-started/upgrade#moving-to-nuxtserver) in the upgrade guide.