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
navigateTo() with external: true generates a server-side HTML redirect body containing a <meta http-equiv="refresh"> tag. The destination URL is only sanitized by replacing " with %22, leaving <, >, &, and ' unencoded. An attacker who can influence the URL passed to navigateTo(url, { external: true }) can break out of the content="…" attribute and inject arbitrary HTML/JavaScript that executes under the application's origin.
This is a different root cause from CVE-2024-34343 (GHSA-vf6r-87q4-2vjf), which addressed javascript: protocol bypass. The issue here is triggered by any valid URL containing >.
Impact
Applications that pass user-controlled input to navigateTo(url, { external: true }) — typically via a ?next= / ?redirect= query parameter used for post-login or "return to" flows — are vulnerable to reflected cross-site scripting. The injected script runs in the context of the application's origin during the server-rendered redirect response, before the meta-refresh fires.
Details
In packages/nuxt/src/app/composables/router.ts, the SSR redirect path builds an HTML response body with only " percent-encoded in the destination URL:
The Location header is normalised through encodeURL() (which uses the URL constructor and correctly percent-encodes attribute-significant characters). The HTML body uses a narrower sanitiser. That mismatch is the root cause.
Proof of concept
Global middleware that forwards a query parameter to navigateTo:
The > after evil.example/x terminates the content="…" attribute, and the <img onerror> tag executes JavaScript in the application's origin before any redirect
occurs.
Patches
Fixed in nuxt@4.4.6 and nuxt@3.21.6 by #35052. The fix percent-encodes the full set of HTML-attribute-significant characters (&, ", ', <, >) before interpolating the URL into the meta-refresh body
Workarounds
If you can't upgrade immediately, validate user-controlled URLs before passing them to navigateTo(url, { external: true }). At minimum, normalise through new URL(input).toString() and reject inputs containing < or > (a normalised URL with these characters is malformed and safe to refuse).
The /__nuxt_island/* endpoint accepts attacker-controlled props query/body parameters and renders any island component without verifying that the URL-resident hash (<Name>_<hashId>.json) was actually issued for those inputs by <NuxtIsland>. The hash is computed and embedded client-side but never validated server-side, so the same path can return materially different responses depending on the query.
Island components are documented as rendering independently of route context - page middleware does not apply to them, and they are intentionally cacheable as a function of their props. This advisory does not treat that contract as a vulnerability. It treats the absence of a binding between the URL the cache keys on and the response served at that URL as one.
Impact
In applications where a CDN or reverse-proxy in front of the app caches /__nuxt_island/* keyed by path only (ignoring query) - a documented misconfiguration class, see GHSA-jvhm-gjrh-3h93 - an attacker can prime the cache for a path with their own choice of props, and subsequent users requesting the same path receive the attacker's rendered HTML rather than the response intended for them. The cache entry persists until normal expiry.
Where the affected island has any prop flowing into an unsafe HTML sink in application code (v-html, innerHTML, a third-party renderer treating a prop as HTML), this becomes stored XSS in the embedding page's origin until the cache entry expires. HttpOnly cookies remain out of reach but anything else in the origin (other cookies, in-origin requests, DOM state) is reachable by the injected script.
Preconditions:
experimental.componentIslands enabled (or the default 'auto' with at least one server / island component in the app).
A shared intermediary cache (CDN, reverse-proxy, edge cache) keyed on path only.
For the XSS pivot specifically: an application-authored island that puts a prop through an unsafe HTML sink.
Without the second precondition, the response shape is per-request and unaffected. Without the third, the worst case is content-swap / inert HTML injection rather than script execution.
Patches
Patched in nuxt@4.4.6 and nuxt@3.21.6 by #35077. The island handler now recomputes the expected hashId from (name, props, context) using the same ohash function <NuxtIsland> already uses to embed the hash in the URL, and rejects requests (HTTP 400) whose URL-resident hash does not match. The response is now a pure function of the request path: a path-keyed shared cache returns the correct response to every requester for that path, and an attacker cannot synthesise a path whose hash matches arbitrary props.
Workarounds
For users unable to upgrade immediately:
Ensure any intermediary cache keys /__nuxt_island/* on the full query string, not on the path alone. This is the recommended configuration regardless.
Audit application-authored islands for props flowing into v-html / innerHTML / similar HTML sinks; treat island props as untrusted user input.
Note on island authentication
[!IMPORTANT]
It's important to remember that route middleware does not run when rendering island components, and islands cannot rely on routing-layer auth. Applications gating sensitive data behind page middleware should enforce that auth inside the island's own data layer (server-only routes, useRequestEvent + manual session checks, etc.) rather than relying on the embedding page's middleware - this was true before this advisory and remains true after it.
A separate advisory addresses *.server.vuepages registered as page_<routeName> islands, where the documented "middleware doesn't run for islands" contract collides with the page's own definePageMeta({ middleware }) declaration in a way that constitutes a genuine bug rather than documented behaviour.
When experimental.componentIslands is enabled (default in Nuxt 4), any .server.vue file under pages/ is automatically registered as a server island under the key page_<routeName> and exposed via the /__nuxt_island/:name endpoint. Until this fix, requests through that endpoint rendered the page component directly via the SSR renderer without instantiating Vue Router, which meant route middleware declared on the page (including definePageMeta({ middleware })) did not run.
For Nuxt applications that gate a .server.vuepage behind route middleware as their sole auth check, an unauthenticated attacker could bypass that check by requesting /__nuxt_island/page_<routeName>_<anyhash> directly and receiving the server-rendered HTML.
Affected configurations
All three conditions must hold for an application to be vulnerable:
experimental.componentIslands is enabled (the default in Nuxt 4; opt-in in Nuxt 3).
The application defines one or more .server.vue files under pages/, registering them as routed pages.
Authentication / authorization for at least one such page is enforced solely via route middleware (middleware/*.ts referenced from definePageMeta), without a server-side check inside the page or its data layer.
Applications that enforce auth inside the island's own data layer (server-only API routes, useRequestEvent + manual session checks, etc.) were not affected. The general "route middleware does not run for non-page island components" behaviour is documented and unchanged; this advisory concerns the .server.vuepage case specifically, where running middleware is the user's clear expectation.
Details
Build (packages/nuxt/src/components/templates.ts): .server.vue pages are registered as island components with page_ prefix, making them addressable through /__nuxt_island/page_<routeName>_<hashId>.
Runtime (packages/nitro-server/src/runtime/handlers/island.ts): the handler resolves the requested island component and renders it via renderer.renderToString(ssrContext). The Vue Router plugin previously short-circuited middleware execution whenever ssrContext.islandContext was set.
The two paths interact so that route middleware declared on the source page never runs.
with middleware/auth.ts blocking unauthenticated access:
##### Direct page request: blocked by middleware
curl -i http://localhost:3000/secret
##### -> 403 / redirect, depending on the middleware##### Island request: middleware did not run before this fix
curl -i 'http://localhost:3000/__nuxt_island/page_secret_anyhash'##### -> 200 OK, body includes <h1>SECRET DATA</h1>
Patches
Patched in nuxt@4.4.6 and nuxt@3.21.6 by #35092. The Vue Router plugin now runs middleware and redirect handling for page_* islands (i.e. islands that originate from .server.vue files in pages/). The island handler propagates middleware-issued responses (~renderResponse), and a new beforeResolve guard returns HTTP 400 when the requested page_<name> does not match the route component the URL resolves to.
Non-page island components are unaffected - they continue to render without route middleware, by design.
Workarounds
If you cannot upgrade immediately:
Enforce authentication inside the .server.vue page itself, not via route middleware. Read the session from useRequestEvent() and throw createError({ statusCode: 401 }) (or redirect) before returning data. This is the recommended pattern for islands regardless of this advisory.
Disable experimental.componentIslands if your app does not use the feature.
If your app must keep route-middleware-only auth, gate the /__nuxt_island/page_* URL prefix at your reverse proxy or in a server middleware.
Nuxt 4.3 brings powerful new features for layouts, caching, and developer experience – plus significant performance improvements under the hood.
📣 Some News
Extended v3 Support
Early this month, I opened a discussion to find out how the upgrade had gone from v3 to v4. I was really pleased to hear how well it had gone for most people.
Having said that, we're committed to making sure no one gets left behind. And so we will continue to provide security updates and critical bug fix releases beyond the previously announced end-of-life date of January 31, 2026, meaning Nuxt v3 will meet its end-of-life on July 31, 2026.
[!TIP]
As usual, today also brings a minor release for v3, with many of the same improvements backported from v4.3.
Preparing for Nuxt 5
We're closer than ever to the releases of Nuxt v5 and Nitro v3. In the coming weeks, the main branch of the Nuxt repository will begin receiving initial commits for Nuxt 5. However, it's still business as usual.
Continue making pull requests to the main branch
We'll backport changes to the 4.x and 3.x branches
Keep an eye out on the Upgrade Guide – we'll be adding details about how you can already start migrating your projects to prepare for Nuxt v4 with future.compatibilityVersion: 5.
🗂️ Route Rule Layouts
But that's enough about the future. We have a lot of good things for you today!
First, you can now set layouts directly in route rules using the new appLayout property (#31092). This provides a centralized, declarative way to manage layouts across your application without scattering definePageMeta calls throughout your pages.
Payload extraction now works with ISR (incremental static regeneration), SWR (stale-while-revalidate) and cache routeRules (#33467). Previously, only pre-rendered pages could generate _payload.json files.
This means:
Client-side navigation to ISR/SWR pages can use cached payloads
CDNs (Vercel, Netlify, Cloudflare) can cache payload files alongside HTML
Fewer API calls during navigation – data can be prefetched and served from the cached payload
exportdefaultdefineNuxtConfig({routeRules: {'/products/**': {isr: 3600,// Revalidate every hour}}})
🧹 Dev Mode Payload Extraction
Related to the above, payload extraction now also works in development mode (#30784). This makes it easier to test and debug payload behavior without needing to run a production build.
[!IMPORTANT]
Payload extraction works in dev mode with nitro.static set to true, or for individual pages which have isr, swr, prerender or cache route rules.
🚫 Disable Modules from Layers
When extending Nuxt layers, you can now disable specific modules that you don't need (#33883). Just pass false to the module's options:
exportdefaultdefineNuxtConfig({extends: ['../shared-layer'],// disable @​nuxt/image from layerimage: false,})
🏷️ Route Groups in Page Meta
Route groups (folders wrapped in parentheses like (protected)/) are now exposed in page meta (#33460). This makes it easy to check which groups a route belongs to in middleware or anywhere you have access to the route.
<script setup lang="ts">
// This page's meta will include: { groups: ['protected'] }useRoute().meta.groups
</script>
Module authors can now use async functions when adding build plugins (#33619):
exportdefaultdefineNuxtModule({asyncsetup(){// Lazy load only when actually neededaddVitePlugin(()=>import('my-cool-plugin').then(r=>r.default()))// No need to load webpack plugin if using ViteaddWebpackPlugin(()=>import('my-cool-plugin/webpack').then(r=>r.default()))}})
This enables true lazy loading of build plugins, avoiding unnecessary code loading when plugins aren't needed.
🚀 Performance Improvements
This release includes several performance optimizations for faster builds:
Hook filters - Internal plugins now use filters to avoid running hooks unnecessarily (#33898)
SSR styles optimization - The nuxt:ssr-styles plugin is now significantly faster (#33862, #33865)
Layer alias transform - Skipped when using Vite (it handles this natively) (#33864)
Route rules compilation - Route rules are now compiled into a client chunk using rou3, removing the need for radix3 in the client bundle and eliminating app manifest fetches (#33920)
🎨 Inline Styles for Webpack/Rspack
The inlineStyles feature now works with webpack and rspack builders (#33966), not just Vite. This enables critical CSS inlining for better Core Web Vitals regardless of your bundler choice.
⚠️ Deprecations
statusCode → status, statusMessage → statusText
In preparation for Nitro v3 and H3 v2, we're moving to use Web API naming conventions (#33912). The old properties still work but are deprecated in advance of v5:
Enabled allowArbitraryExtensions by default in TypeScript config (#34084)
Added noUncheckedIndexedAccess to server tsconfig for safer typing (#33985)
[!IMPORTANT]
Enabling noUncheckedIndexedAccess in the Nitro server TypeScript config improves type safety but may surface new type errors in your server code. This change was necessary because Nuxt's app context performs type checks on server routes (learn more).
While we recommend keeping this enabled for better type safety, you can disable it if needed:
Clarified type-checking context limitations for server routes (#33964)
🎉 Nuxt 3.21.0
Alongside v4.3.0, we're releasing Nuxt v3.21.0 with many of the same improvements backported to the 3.x branch. This release includes:
All the same features: Route rule layouts, ISR payload extraction, layout props with setPageLayout, #server alias, draggable error overlay, and more
All performance improvements: SSR styles optimization, hook filters, and route rules compilation
Module disabling: Disable layer modules by setting options to false
Critical bug fixes: Async data reactivity in Options API, useCookie number parsing, head component deduplication, and more
✅ Upgrading
Our recommendation for upgrading is to run:
npx nuxt upgrade --dedupe
# or, if you are upgrading to v3.21
npx nuxt@latest upgrade --dedupe --channel=v3
This will deduplicate your lockfile and help ensure you pull in updates from other dependencies that Nuxt relies on, particularly in the unjs ecosystem.
[!TIP]
Check out our upgrade guide if upgrading from an older version.
Next steps: Take a moment to review the security alert above. Review
the linked package source code to understand the potential risk. Ensure the
package is not malicious before proceeding. If you're unsure how to proceed,
reach out to your security team or ask the Socket team for help at
support@socket.dev.
Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.
Mark the package as acceptable risk. To ignore this alert only
in this pull request, reply with the comment
@SocketSecurity ignore npm/ioredis@5.11.0. You can
also ignore all packages with @SocketSecurity ignore-all.
To ignore an alert for all future pull requests, use Socket's Dashboard to
change the triage state of this alert.
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.
This PR contains the following updates:
4.1.0→4.4.6Nuxt: Reflected XSS in
navigateTo()external redirectCVE-2026-45669 / GHSA-fx6j-w5w5-h468
More information
Details
Summary
navigateTo()withexternal: truegenerates a server-side HTML redirect body containing a<meta http-equiv="refresh">tag. The destination URL is only sanitized by replacing"with%22, leaving<,>,&, and'unencoded. An attacker who can influence the URL passed tonavigateTo(url, { external: true })can break out of thecontent="…"attribute and inject arbitrary HTML/JavaScript that executes under the application's origin.This is a different root cause from CVE-2024-34343 (GHSA-vf6r-87q4-2vjf), which addressed
javascript:protocol bypass. The issue here is triggered by any valid URL containing>.Impact
Applications that pass user-controlled input to
navigateTo(url, { external: true })— typically via a?next=/?redirect=query parameter used for post-login or "return to" flows — are vulnerable to reflected cross-site scripting. The injected script runs in the context of the application's origin during the server-rendered redirect response, before the meta-refresh fires.Details
In
packages/nuxt/src/app/composables/router.ts, the SSR redirect path builds an HTML response body with only"percent-encoded in the destination URL:The
Locationheader is normalised throughencodeURL()(which uses theURLconstructor and correctly percent-encodes attribute-significant characters). The HTML body uses a narrower sanitiser. That mismatch is the root cause.Proof of concept
Global middleware that forwards a query parameter to
navigateTo:Request:
Response body:
The
>afterevil.example/xterminates thecontent="…"attribute, and the<img onerror>tag executes JavaScript in the application's origin before any redirectoccurs.
Patches
Fixed in
nuxt@4.4.6andnuxt@3.21.6by #35052. The fix percent-encodes the full set of HTML-attribute-significant characters (&,",',<,>) before interpolating the URL into the meta-refresh bodyWorkarounds
If you can't upgrade immediately, validate user-controlled URLs before passing them to
navigateTo(url, { external: true }). At minimum, normalise throughnew URL(input).toString()and reject inputs containing<or>(a normalised URL with these characters is malformed and safe to refuse).Severity
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:L/VI:L/VA:N/SC:L/SI:L/SA:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Nuxt:
__nuxt_islandendpoint does not bind responses to request props, enabling shared-cache poisoningCVE-2026-46342 / GHSA-g8wj-3cr3-6w7v
More information
Details
Summary
The
/__nuxt_island/*endpoint accepts attacker-controlledpropsquery/body parameters and renders any island component without verifying that the URL-resident hash (<Name>_<hashId>.json) was actually issued for those inputs by<NuxtIsland>. The hash is computed and embedded client-side but never validated server-side, so the same path can return materially different responses depending on the query.Island components are documented as rendering independently of route context - page middleware does not apply to them, and they are intentionally cacheable as a function of their props. This advisory does not treat that contract as a vulnerability. It treats the absence of a binding between the URL the cache keys on and the response served at that URL as one.
Impact
In applications where a CDN or reverse-proxy in front of the app caches
/__nuxt_island/*keyed by path only (ignoring query) - a documented misconfiguration class, see GHSA-jvhm-gjrh-3h93 - an attacker can prime the cache for a path with their own choice of props, and subsequent users requesting the same path receive the attacker's rendered HTML rather than the response intended for them. The cache entry persists until normal expiry.Where the affected island has any prop flowing into an unsafe HTML sink in application code (
v-html,innerHTML, a third-party renderer treating a prop as HTML), this becomes stored XSS in the embedding page's origin until the cache entry expires.HttpOnlycookies remain out of reach but anything else in the origin (other cookies, in-origin requests, DOM state) is reachable by the injected script.Preconditions:
experimental.componentIslandsenabled (or the default'auto'with at least one server / island component in the app).Without the second precondition, the response shape is per-request and unaffected. Without the third, the worst case is content-swap / inert HTML injection rather than script execution.
Patches
Patched in
nuxt@4.4.6andnuxt@3.21.6by #35077. The island handler now recomputes the expectedhashIdfrom(name, props, context)using the sameohashfunction<NuxtIsland>already uses to embed the hash in the URL, and rejects requests (HTTP 400) whose URL-resident hash does not match. The response is now a pure function of the request path: a path-keyed shared cache returns the correct response to every requester for that path, and an attacker cannot synthesise a path whose hash matches arbitrary props.Workarounds
For users unable to upgrade immediately:
/__nuxt_island/*on the full query string, not on the path alone. This is the recommended configuration regardless.v-html/innerHTML/ similar HTML sinks; treat island props as untrusted user input.Note on island authentication
A separate advisory addresses
*.server.vuepages registered aspage_<routeName>islands, where the documented "middleware doesn't run for islands" contract collides with the page's owndefinePageMeta({ middleware })declaration in a way that constitutes a genuine bug rather than documented behaviour.Severity
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:L/VI:L/VA:N/SC:L/SI:L/SA:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Nuxt's route middleware is not enforced when rendering
.server.vuepages via/__nuxt_island/page_*CVE-2026-47200 / GHSA-hg3f-28rg-4jxj
More information
Details
Summary
When
experimental.componentIslandsis enabled (default in Nuxt 4), any.server.vuefile underpages/is automatically registered as a server island under the keypage_<routeName>and exposed via the/__nuxt_island/:nameendpoint. Until this fix, requests through that endpoint rendered the page component directly via the SSR renderer without instantiating Vue Router, which meant route middleware declared on the page (includingdefinePageMeta({ middleware })) did not run.For Nuxt applications that gate a
.server.vuepage behind route middleware as their sole auth check, an unauthenticated attacker could bypass that check by requesting/__nuxt_island/page_<routeName>_<anyhash>directly and receiving the server-rendered HTML.Affected configurations
All three conditions must hold for an application to be vulnerable:
experimental.componentIslandsis enabled (the default in Nuxt 4; opt-in in Nuxt 3)..server.vuefiles underpages/, registering them as routed pages.middleware/*.tsreferenced fromdefinePageMeta), without a server-side check inside the page or its data layer.Applications that enforce auth inside the island's own data layer (server-only API routes,
useRequestEvent+ manual session checks, etc.) were not affected. The general "route middleware does not run for non-page island components" behaviour is documented and unchanged; this advisory concerns the.server.vuepage case specifically, where running middleware is the user's clear expectation.Details
packages/nuxt/src/components/templates.ts):.server.vuepages are registered as island components withpage_prefix, making them addressable through/__nuxt_island/page_<routeName>_<hashId>.packages/nitro-server/src/runtime/handlers/island.ts): the handler resolves the requested island component and renders it viarenderer.renderToString(ssrContext). The Vue Router plugin previously short-circuited middleware execution wheneverssrContext.islandContextwas set.Proof of concept
Given a page
app/pages/secret.server.vue:with
middleware/auth.tsblocking unauthenticated access:Patches
Patched in
nuxt@4.4.6andnuxt@3.21.6by #35092. The Vue Router plugin now runs middleware and redirect handling forpage_*islands (i.e. islands that originate from.server.vuefiles inpages/). The island handler propagates middleware-issued responses (~renderResponse), and a newbeforeResolveguard returns HTTP 400 when the requestedpage_<name>does not match the route component the URL resolves to.Non-page island components are unaffected - they continue to render without route middleware, by design.
Workarounds
If you cannot upgrade immediately:
.server.vuepage itself, not via route middleware. Read the session fromuseRequestEvent()andthrow createError({ statusCode: 401 })(or redirect) before returning data. This is the recommended pattern for islands regardless of this advisory.experimental.componentIslandsif your app does not use the feature./__nuxt_island/page_*URL prefix at your reverse proxy or in a server middleware.Severity
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
Release Notes
nuxt/nuxt (nuxt)
v4.4.6Compare Source
👉 Changelog
compare changes
🩹 Fixes
resolveComponentcalls in jsx blocks (#35028)useFetchkey even withwatch: false(#35002)@babel/plugin-syntax-typescriptas optional peer dep (#35041)setPageLayoutprops on same-path navigation (#35055)useLoadingIndicatorproperties as readonly (#35062)statusCodefor nitro v2 compatibility (952f6841e)tsConfig.excludein legacytsconfig.json(#35079)💅 Refactors
📖 Documentation
🏡 Chore
✅ Tests
🤖 CI
test:enginesfails (3025e561e)❤️ Contributors
v4.4.5Compare Source
👉 Changelog
compare changes
🔥 Performance
isIgnoredrelative (#35015)🩹 Fixes
clientServerwithssr: false(#34959)/+ overridessr: true(#34990)optimizeDeps.includein pre-bundle hint (#34976).envbefore resolving nuxt schema (#34958)serverHandlersarray afternitro:config(#34985)getCachedDatafor concurrent callers sharing a key (#34999)useFetch(#35003)📖 Documentation
🏡 Chore
jiti(c8102228f)@vue/compiler-sfc(cd404a14c)✅ Tests
buildDirper matrix project for shared fixtures (#35007)❤️ Contributors
v4.4.4Compare Source
v4.4.2Compare Source
v4.3.1Compare Source
👉 Changelog
compare changes
🩹 Fixes
x-nitro-prerenderheader (#34202)server/forbuilder:watchhook (#34208)error.messagefor fatal errors (#34226)#appbarrel export in keyed functions (#34199)datetime in` (#33992)nuxt/schema(#34255)meta.name(#34263)#componentsimport mapping conflict for packages outside rootDir (#34139)nuxt/schemaonce more (552bbd8d1)💅 Refactors
genObjectKeyto omit unnecessary quotes (#34245)ComponentPropshelper to extract layout props (#34248)📖 Documentation
nitroAutoImports(#34182)#serverandrootDir(#34259)keyedComposables(#34201)🏡 Chore
pxfromwidthattribute (8d1cbb27a)✅ Tests
<NuxtPage>navigation (048efc030)❤️ Contributors
v4.3.0Compare Source
Nuxt 4.3 brings powerful new features for layouts, caching, and developer experience – plus significant performance improvements under the hood.
📣 Some News
Extended v3 Support
Early this month, I opened a discussion to find out how the upgrade had gone from v3 to v4. I was really pleased to hear how well it had gone for most people.
Having said that, we're committed to making sure no one gets left behind. And so we will continue to provide security updates and critical bug fix releases beyond the previously announced end-of-life date of January 31, 2026, meaning Nuxt v3 will meet its end-of-life on July 31, 2026.
Preparing for Nuxt 5
We're closer than ever to the releases of Nuxt v5 and Nitro v3. In the coming weeks, the
mainbranch of the Nuxt repository will begin receiving initial commits for Nuxt 5. However, it's still business as usual.mainbranch4.xand3.xbranchesKeep an eye out on the Upgrade Guide – we'll be adding details about how you can already start migrating your projects to prepare for Nuxt v4 with
future.compatibilityVersion: 5.🗂️ Route Rule Layouts
But that's enough about the future. We have a lot of good things for you today!
First, you can now set layouts directly in route rules using the new
appLayoutproperty (#31092). This provides a centralized, declarative way to manage layouts across your application without scatteringdefinePageMetacalls throughout your pages.This might be useful for:
📦 ISR/SWR Payload Extraction
Payload extraction now works with ISR (incremental static regeneration), SWR (stale-while-revalidate) and cache
routeRules(#33467). Previously, only pre-rendered pages could generate_payload.jsonfiles.This means:
🧹 Dev Mode Payload Extraction
Related to the above, payload extraction now also works in development mode (#30784). This makes it easier to test and debug payload behavior without needing to run a production build.
🚫 Disable Modules from Layers
When extending Nuxt layers, you can now disable specific modules that you don't need (#33883). Just pass
falseto the module's options:🏷️ Route Groups in Page Meta
Route groups (folders wrapped in parentheses like
(protected)/) are now exposed in page meta (#33460). This makes it easy to check which groups a route belongs to in middleware or anywhere you have access to the route.This provides a clean, convention-based approach to route-level authorization without needing to add
definePageMetato every protected page.🎨 Layout Props with
setPageLayoutThe
setPageLayoutcomposable now accepts a second parameter to pass props to your layout (#33805):🔧
#serverAliasA new
#serveralias provides clean imports within your server directory (#33870), similar to how#sharedworks:The alias includes import protection – you can't accidentally import
#servercode from client or shared contexts.🪟 Draggable Error Overlay
The development error overlay introduced in Nuxt 4.2 is now draggable and can be minimized (#33695). You can:
This is a quality-of-life improvement when you're iterating on fixes and don't want the overlay blocking your view.
https://github.com/user-attachments/assets/nuxt_4-3_error_demo.mp4
⚙️ Async Plugin Constructors
Module authors can now use async functions when adding build plugins (#33619):
This enables true lazy loading of build plugins, avoiding unnecessary code loading when plugins aren't needed.
🚀 Performance Improvements
This release includes several performance optimizations for faster builds:
nuxt:ssr-stylesplugin is now significantly faster (#33862, #33865)rou3, removing the need forradix3in the client bundle and eliminating app manifest fetches (#33920)🎨 Inline Styles for Webpack/Rspack
The
inlineStylesfeature now works with webpack and rspack builders (#33966), not just Vite. This enables critical CSS inlining for better Core Web Vitals regardless of your bundler choice.statusCode→status,statusMessage→statusTextIn preparation for Nitro v3 and H3 v2, we're moving to use Web API naming conventions (#33912). The old properties still work but are deprecated in advance of v5:
🐛 Bug Fixes
Notable fixes in this release:
keyattribute (#33958, #33963)useCookieunsafe number parsing during decode (#34007)NuxtPagenot re-rendering when nestedNuxtLayouthas layouts disabled (#34078)allowArbitraryExtensionsby default in TypeScript config (#34084)noUncheckedIndexedAccessto server tsconfig for safer typing (#33985)📚 Documentation
🎉 Nuxt 3.21.0
Alongside v4.3.0, we're releasing Nuxt v3.21.0 with many of the same improvements backported to the 3.x branch. This release includes:
setPageLayout,#serveralias, draggable error overlay, and morefalseuseCookienumber parsing, head component deduplication, and more✅ Upgrading
Our recommendation for upgrading is to run:
npx nuxt upgrade --dedupe # or, if you are upgrading to v3.21 npx nuxt@latest upgrade --dedupe --channel=v3This will deduplicate your lockfile and help ensure you pull in updates from other dependencies that Nuxt relies on, particularly in the unjs ecosystem.
👉 Changelog
compare changes
🚀 Enhancements
#serveralias for server directory imports (#33870)crosswstypes (5b16a51f5)false(#33883)moduleDependenciesas an async function (#33504)appLayoutin route rules (#31092)setPageLayout(#33805)🔥 Performance
nuxt:ssr-stylesplugin (#33862)🩹 Fixes
router.replacein page hmr (#33897)page:loading:endin cache if already called (7789f73bd)NUXT_VITE_NODE_OPTIONS(41a564d23)appMiddlewarereferences invalid key (323f27bc8)nuxt/meta(01c2c9b13)keyfor tag deduplication in<Head>component (#33958)build.transpilewhen initialising vite (#33868)onUpgradearguments with types (#33988)rou3(2df4e1ae3)noUncheckedIndexedAccessto server tsconfig (#33985)shared/context (#33978)useRequestFetch(#33976)h3types to auto-imports (#34035)nuxt/schema(a6a044d81)NuxtPagewhen nestedNuxtLayouthas explicitly disabled layouts (#34078)allowArbitraryExtensionsby default (#34084)useAsyncDatadebounced execute post watcher flush (#34125)typeFromsupport forimports.d.tstemplate exports (#34135)hydrate-nevercomponents (#34132)💅 Refactors
defu+consola(322dae3e0).tsfile extensions to relative imports (80778c0cd)<>toas(f1713850c)~prefix for internal ssrContext properties (#33896)status/statusText+ deprecate old props (#33912)📖 Documentation
Module Author Guides(#33803)useHeadreturn type (#33857)statusText(#32834)defineWrappedResponseHandler(#33952)addImportsexample (#34011)config.experimentalproperties (#34069)nullfromgetCachedDatatrigger (f7cf3747e)useStatedocs ([#34105](https://redirect.github.com/nuxt/nuxt/pull/Configuration
📅 Schedule: (UTC)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about these updates again.
This PR was generated by Mend Renovate. View the repository job log.