👀 Highlights
❄️ Snowfall!
Happy holidays! You'll notice when you start Nuxt that (if you're in the Northern Hemisphere) there's some snow on the loading screen (#29871).
⚡️ Vite 6 included
Nuxt v3.15 includes Vite 6 for the first time. Although this is a major version, we expect that this won't be a breaking change for Nuxt users (see full migration guide). However, please take care if you have dependencies that rely on a particular Vite version.
One of the most significant changes with Vite 6 is the new Environment API, which we hope to use in conjunction with Nitro to improve the server dev environment. Watch this space!
You can read the full list of changes in the Vite 6 changelog.
🪵 Chromium devtools improvements
We talk a lot about the Nuxt DevTools, but v3.15 ships with better integration in dev mode for Chromium-based browser devtools.
We now use the Chrome DevTools extensibility API to add support for printing nuxt hook timings in the browser devtools performance panel.
🗺️ Navigation mode for callOnce
callOnce` is a built-in Nuxt composable for running code only once. For example, if the code runs on the server it won't run again on the client. But sometimes you do want code to run on _every navigation_ - just avoid the initial server/client double load. For this, there's a new
mode: 'navigation'` option that will run the code only once _per navigation_. (See #30260 for more info.)
await callOnce(() => counter.value++, { mode: 'navigation' })
🥵 HMR for templates, pages + page metadata
We now implement hot module reloading for Nuxt's virtual files (like routes, plugins, generated files) as well as for the content of page metadata (within a definePageMeta
macro) (#30113).
This should mean you have a faster experience in development, as well as not needing to reload the page when making changes to your routes.
📋 Page meta enhancements
We now support extracting extra page meta keys (likely used by module authors) via experimental.extraPageMetaExtractionKeys
(#30015). This enables module authors to use this information at build time, in the pages:resolved
hook.
We also now support local functions in definePageMeta
(#30241). This means you can do something like this:
function validateIdParam(route) {
return !!(route.params.id && !isNaN(Number(route.params.id)))
}
definePageMeta({
validate: validateIdParam,
})
🔥 Performance improvements
We now preload the app manifest in the browser if it will be used when hydrating the app (#30017).
We'll also tree shake vue-router's hash mode history out of your bundle if we can - specifically, if you haven't customised your app/router.options.ts
(#30297).
🐣 v4 updates
If A few more changes shipped for the new defaults for v4, including only inlining styles by default for Vue components (#30305).
✅ Upgrading
As usual, our recommendation for upgrading is to run:
npx nuxi@latest upgrade --force
This will refresh your lockfile as well, and ensures that you pull in updates from other dependencies that Nuxt relies on, particularly in the unjs ecosystem.
👉 Changelog
🚀 Enhancements
- deps: Update dependency vite to v6 (3.x) (#30044)
- kit: Allow module default options to be async (#29980)
- nuxt: Add new types to vue preset (#29819)
- nuxt: Experimental
extraPageMetaExtractionKeys
(#30015) - nuxt,schema: Allow setting serialisable vue app config (#28873)
- nuxt: Print nuxt hook timings in browser devtools (#29922)
- nuxt: Support vue directive auto-imports within unimport (#29818)
- schema: Add snow effect on loading screen in winter (#29871)
- nuxt: Support local functions in
definePageMeta
(#30241) - nuxt: Add
mode: 'navigation'
tocallOnce
(#30260)
🔥 Performance
- nuxt: Preload app manifest (#30017)
- nuxt: Use static
hashMode
option (#30297) - vite: Use vite to clear screen (#30315)
- schema: Only inline styles for vue components (#30305)
- nuxt: Remove useId from composable key plugin (#30328)
🩹 Fixes
- nuxt: Check if nuxt link observer is null (#30038)
- nuxt: Unref the default value of asyncData when clearing (#30041)
- kit: Re-export
addServerTemplate
(a02af2348) - Remove unused dependencies and tidy project (#30043)
- vite: Add back some dev-bundler dependencies (976024f16)
- nuxt: Do not persist
extraExtractionKeys
on runtimeroute.meta
(ae9f42f4a) - nuxt: Allow array/object
style
value for head components (#29999) - nuxt: Tidy up remnants of previous
useId
implementation (40f437d25) - kit,nuxt: Provide
buildDir
tonormalizeTemplate
(#30115) - kit: Add better logging for non-resolved modules (#30116)
- nuxt: Correct return type of
useRequestFetch
(#30117) - nuxt,vite: Hmr for templates, pages + page metadata (#30113)
- nuxt: Use
nitropack
rather thannitro
import (2d5b53b23) - kit: Use resolved module paths for transpile + modulesDir (#30136)
- Update
engines.node
to match dependencies (#30139) - schema: Allow
routerOptions.history
to return null (#30192) - nuxt: Render client page directly when not hydrating (#30061)
- nuxt: Use
useId
for island client component teleport id (#30151) - nuxt: De-default async layout components (#30203)
- nuxt: Correct types for
nuxt
andnuxt/app
(#30148) - nuxt,schema: Allow showing spa loader til after hydration (#29776)
- nuxt: Remove whitespace around spa loading template (070bd103c)
- nuxt: Hoist environment types (#30230)
- schema: Hoist nitro runtime types (73761dade)
- nuxt: Ensure
getRouteRules
works with nitro signature (#30277) - nuxt: Respect
replace
in middleware withnavigateTo
(#30283) - nuxt: Update import paths for
nitropack
(f220314a5) - nuxt: Don't use
<RouterLink>
for links starting with#
(#30190) - vite: Ignore optimising
#app-manifest
(ec613e533) - nuxt: Use
useId
forclient-fallback
component uid (#30314) - schema: Gate inline style change behind v4 check (ceac86e34)
- nuxt: Do not resolve deep imports for
@vitest/
(4171a1076) - kit: Initialize tsconfig paths in
addTemplate
if undefined (#30348) - nuxt: Treat client useAsyncData calls as async boundaries (#30343)
- nuxt: Initialise
import.meta.hot.data
(b1cf5781d)
💅 Refactors
- Move
composable-keys
plugin into nuxt core (#30029) - nuxt: Simplify and improve core plugins that parse ast (#30088)
- nuxt: Prefix all core modules with
nuxt:
(#30028)
📖 Documentation
- Remove extra new line in frontmatter (#30031)
- Text capitalization for titles (#30054)
- Mention that type checking can happen in dev (#30012)
- Fix typos in punctuation (#30006)
- Remove duplicate information about preprocessor variables (#30002)
- Format text case for consistency (#30073)
- Add a section about
event.waitUntil
(#29583) - Improve wording (#30106)
- Update configuration files format (#30087)
- Update links to
vite.dev
(#30111) - Fix incorrect vite docs link (#30112)
- Add note about using bun runtime (#30019)
- Add giget limitation in nuxt layers documentation (#30122)
- Correct vite link (#30135)
- Add
nuxi upgrade
channel flag (#30184) - Add note about awaiting
useLazyFetch
(#30171) - Add missing comma in upgrade doc code sample (#30189)
- Added options and option definitions for sourcemap (#30201)
- Add shared directory documentation (#29816)
- Document
vite.css.preprocessorMaxWorkers
(eb1ba017c) - Handle zero-length string (cf74b4c98)
- Update nitro links + fix link checking (#30228)
- Add a note about
compatibilityVersion
feature flag (#30274) - Update auto-imports to advertise the scan feature (#30292)
- Update
nuxi
command pages (#30199) - Update migration documentation for
inlineStyles
(2660bffbc) - Add bluesky link (#30322)
- Add recipe for session and authentication (#27287)
- Fix filename for prerendering page (#30333)
- Add spacing (#30331)
🏡 Chore
- Ignore typescript update for now (10f08011c)
- Refresh lockfile (2cdd04c62)
- Don't ignore typescript, but upgrade it separately (149477467)
- Pin
unimport
(7ee455969) - Upgrade unimport separately (dcf8bda04)
- Add
installed-check
dependency (0e84cb9a4) - Remove unimport from isolation (9d7f70ec0)
- Migrate renovate config (#30214)
- Update bundle size snapshot (b88ad0765)
- Downgrade
engines.node
to reflect only deps (d3d276919) - Remove (unused)
rimraf
(cf9d82c5a) - Remove outdated comment (#30324)
- Cleanup renovate (6e682b1ff)
- Document
div
wrapper in client-only page (#30359)
✅ Tests
- Add additional attw test for built packages (#30206)
- Improve assertions for spa loading tests (80bd2d2ec)
- Bump bundle size snapshot (d545f9e96)
- Try to improve spa preloader tests (08219a502)
- Ensure dev server is loaded before running tests (f66cf928e)
🤖 CI
- Ignore dev-dependencies for engines.node (e366a6feb)
- Analyse github actions with codeql (#30293)
- Exclude file that codeql cannot analyse (7e03b08a6)
❤️ Contributors
- Daniel Roe (@danielroe)
- Julien Huang (@huang-julien)
- Mehmet (@productdevbook)
- @beer (@iiio2)
- Nishant Aanjaney Jalan (@cybercoder-naj)
- David Nahodyl (@Smef)
- Vuk Marjanovic (@vmrjnvc)
- Alexander Lichter (@TheAlexLichter)
- Bobbie Goede (@BobbieGoede)
- Connor Roberts (@murshex)
- Matej Černý (@cernymatej)
- derHodrig (@derHodrig)
- Martins Zeltins (@martinszeltins)
- Matt (@matt-clegg)
- Nikolay (@RokeAlvo)
- Joaquín Sánchez (@userquin)
- bjoaquinc (@bjoaquinc)
- wzc520pyfm (@wzc520pyfm)
- Kraig Burrows (@zync09)
- Lucie (@lihbr)
- Harlan Wilton (@harlan-zw)
- 一纸忘忧 (@ikxin)
- Sébastien LeBlanc (@mrleblanc101)
- Teena (@franklin-tina)
- skmedix (@skmedix)
- Daniel Rentz (@danielrentz)
- Inesh Bose (@ineshbose)
- Felix Gabler (@felixgabler)
- Damian Głowala (@DamianGlowala)
- mianlang (@mianlang)
- Tamás H. (@Tamas-hi)
- xjccc (@xjccc)
- Guillaume Chau (@Akryum)
- Farnabaz (@farnabaz)