v4.0.0 - #1482
Draft
github-actions[bot] wants to merge 2 commits into
Draft
v4.0.0#1482github-actions[bot] wants to merge 2 commits into
github-actions[bot] wants to merge 2 commits into
Conversation
commit: |
Contributor
Author
CLI benchmark
Full report
|
| Setting | Value |
|---|---|
| Baseline | ref:11226dacafa9e0506c123073464ea4e0a4515e54 (v4.0.0-alpha.1) |
| Head | local packages/nuxt-cli at c3bfdc9 (v4.0.0) |
| Node | v24.21.0 |
| OS | Linux 6.17.0 (kernel 6.17.0-1022-azure) |
| CPU | AMD EPYC 9V45 96-Core Processor x 4 |
| Memory | 15.6 GB |
| Load average at start | 1.01, 0.27, 0.09 |
| Run started | 2026-09-25T23:10:25.097Z |
Cold CLI startup
Median of 15 interleaved runs per command, one warmup discarded.
| Command | baseline v4.0.0-alpha.1 median | head v4.0.0 median | Delta | baseline v4.0.0-alpha.1 min / p95 | head v4.0.0 min / p95 |
|---|---|---|---|---|---|
nuxt --version |
41 ms | 40 ms | -3.9% | 39 ms / 44 ms | 38 ms / 42 ms |
nuxt --version (first output byte) |
38 ms | 36 ms | -4.6% | 36 ms / 40 ms | 35 ms / 39 ms |
nuxt --help |
81 ms | 82 ms | +1.3% | 77 ms / 83 ms | 79 ms / 88 ms |
nuxt --help (first output byte) |
77 ms | 78 ms | +1.5% | 73 ms / 79 ms | 75 ms / 84 ms |
nuxt dev --help |
63 ms | 62 ms | -1.1% | 60 ms / 68 ms | 58 ms / 65 ms |
nuxt dev --help (first output byte) |
59 ms | 58 ms | -1.4% | 56 ms / 64 ms | 54 ms / 61 ms |
nuxt <unknown-command> (no-op) |
88 ms | 88 ms | -0.1% | 83 ms / 97 ms | 85 ms / 101 ms |
nuxt <unknown-command> (no-op) (first output byte) |
84 ms | 84 ms | +0.1% | 79 ms / 93 ms | 81 ms / 96 ms |
Module load cost
Counted with a module.registerHooks load hook, compile cache disabled. Counts every JS module actually evaluated on that code path (built-ins excluded, native addons excluded).
| Command | baseline v4.0.0-alpha.1 modules | head v4.0.0 modules | Delta | baseline v4.0.0-alpha.1 source bytes | head v4.0.0 source bytes | Delta |
|---|---|---|---|---|---|---|
nuxt --version |
38 | 38 | 0.0% | 296.7 kB | 296.7 kB | -0.0% |
nuxt --help |
144 | 144 | 0.0% | 958.9 kB | 958.9 kB | -0.0% |
nuxt dev --help |
63 | 63 | 0.0% | 449.8 kB | 449.8 kB | -0.0% |
Install footprint and published tarball
Each version installed on its own into an empty project with nothing but @nuxt/cli as a dependency, so the tree is exactly the CLI and its transitive dependencies. npm cache is warm and the registry is only consulted for metadata, so install wall time is indicative, not a network benchmark.
| Metric | baseline v4.0.0-alpha.1 | head v4.0.0 | Delta |
|---|---|---|---|
Direct dependencies of @nuxt/cli |
22 | 22 | 0.0% |
| Packages in the installed tree (unique name@version) | 38 | 38 | 0.0% |
| Unique package names | 38 | 38 | 0.0% |
| Package directories on disk (cross-check) | 31 | 31 | 0.0% |
Installed node_modules on disk |
2.39 MB | 2.39 MB | -0.0% |
| Installed files | 420 | 420 | 0.0% |
| Install wall time (warm npm cache, median of 3) | 737 ms | 736 ms | -0.2% |
| Published tarball (packed) | 236.0 kB | 236.0 kB | -0.0% |
| Published tarball (unpacked) | 769.0 kB | 769.0 kB | -0.0% |
| Files in tarball | 97 | 97 | 0.0% |
Interleaved runs on a shared runner: trust the deltas, not the absolute timings. The dev, restart and build suites run locally via pnpm bench:cli.
This branch has not been deployed
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
👀 Highlights
Nuxt CLI v4 is a performance, feature and DX-focused release.
It ships alongside Nuxt 4.6, and alongside resolving almost all of the open issues, we've focused on the
nuxt devexperience: how quickly it starts, the information it shows you, and what happens when something goes wrong.It's also a lot smaller. 😁
There's quite a bit to cover (more than 200 commits since v3.37), so here are just some of the things that we're particularly excited about! 🎉
🐞 Better dev-time errors with
my-badDev-time errors are now rendered with
my-bad, which replacesyouch(#1518).When a page fails to render, you get your own page's error state along with a small overlay in the corner. Expanding it shows the source-mapped code frame in your file, the call stack (with framework frames folded away by default), the Vue component trace, the request and environment, and the server logs that led up to the error.
There's a Copy error button with a few formats, including a prompt you can hand straight to an agent:
The CLI now also hosts a single live error channel, mounted at
devServer.errorChannel(default/__nuxt_dev__/error). Because it lives in the CLI rather than inside Nuxt, it survives restarts of the process serving your app, and it can report errors that happen before Nuxt is up at all. So if yournuxt.confighas a syntax error, or a module can't be loaded, you get a proper error page in the browser, which reloads by itself once you've fixed it:Each error is rendered once, rather than at several levels (Vite plugin, Nuxt error handler, h3). The terminal gets the same treatment, with a code frame and folded dependency frames:
Note
The in-app overlay, source-mapped SSR stack traces and component traces need Nuxt 4.6 (nuxt/nuxt#36258). With an older Nuxt, the CLI still renders errors it sees itself (such as startup and config failures) with
my-bad, but other errors will be rendered as before withyouch.The error channel is only served in full to loopback peers. If you expose your dev server to the network, remote peers get a scoped view without request details or error history (#1547, #1552, #1554).
🖥️ An interactive terminal UI for
nuxt devI am very excited about this one. In an interactive terminal,
nuxt devnow renders a panel pinned to the bottom of the screen with your URLs, live startup progress, the current status and a row of shortcuts, while logs fold away above it (#1463).Everything is a single keypress away:
r/shift-royil/enpc?qEvery request the dev server forwards is now tagged with an id, so logs and errors are attributed to the request that caused them, and you can drill into a request from the
nview to see what it logged (2d9f1a8, #1557, #1558, #1474).When something inside the dev server needs your input (for example,
useScript()offering to install@nuxt/scripts), the panel steps aside and lets you answer it (#1488):This also adds three primitives for modules that want to play nicely with the new UI:
withTerminal()to borrow the terminal,startTask()for a spinner on the status line, andnotify()for a notice that lands in the log history. (To use this, make sure your module has@nuxt/kitv4.6 as a dependency...)Tip
The UI falls back to a plain stream of logs when the output isn't a terminal, in CI, when a debugger is attached, or when the terminal is too small. Pass
--no-tui(or setNUXT_TUI=plainfor good) if you prefer the classic output.We've also tried to make the dev server surface more information about what it's doing:
81a5de1).nuxt.configchange it lists the keys that actually changed (feat(dev): report why the dev server reloaded or restarted #1402, feat(dev): print changednuxt.configkeys on a config-triggered reload #1403). Saves that change nothing no longer restart the server at all (perf(dev): stop saves that change nothing from restarting the dev server #1435).nuxt build, which reports its phases and where the time went (feat(build): report build phases and where the time went #1490).⚡️ Faster startup, smaller install
As mentioned above, we've spent a lot of time on the size and startup time of the CLI and have cleared a way a lot of unnecessary, old and deprecated dependencies.
@nuxt/clihas gone from 70 packages to 31 and from 13.1 MB to 3.5 MB installed, and the globalnuxipackage from 6.0 MB to 0.8 MB. We've swapped out a number of dependencies for Node built-ins or smaller alternatives (picocolorsfornode:utilstyleText,semverforverkit,fuse.jsforfuzzysort,magicastforoxc,ofetchfor nativefetch,source-map-jsfor Node'sSourceMap), stopped bundlingjiti, and keptyouch, registry lookups and the update check off the startup path (#1379, #1409, #1413, #1416, #1425, #1430, #1443).@nuxt/cliinstall size@nuxt/clidependenciesnuxiinstall sizenuxt --helpnuxi --help(global)nuxt dev: first paintnuxt dev: port boundnuxt dev: first page servednuxt dev: memory at rest (Linux)Note
This was measured on a minimal
pnpm create nuxtapp with Nuxt 4.5.2, Node 24.15 on arm64 Linux, median of 10 runs.A few of the changes behind this:
88de5da,11226da).SO_REUSEPORTand hands it to the new process, rather than dropping connections while it comes back up (fix(dev): keep serving during a hard restart usingSO_REUSEPORT#1404).NUXT_DEV_FORK_POOL_SIZE), and only start warming it once you change a file (1897968,9a10f71).🤖 Friendlier to agents (and to people with too many terminals)
A running dev server now records itself in a lock file in
.nuxt/. That means a secondnuxt devfor the same project reports the server that is already running rather than racing it for a port, and can take it over when that makes sense (#1414):nuxt devwhile another non-interactive server is running, it takes over (loudly).nuxt devrefuses and tells the agent where your server is.--takeoverto force it.The lock file also powers two new commands that talk to the running server without you needing to know the port (#1417, #1432):
nuxt curl /api/hello nuxt curl /api/users -X POST -d '{"name":"daniel"}' nuxt task list nuxt task run db:migrate --payload.force=truenuxt curlpretty-prints and highlights responses in a terminal, and exits with22on HTTP errors, just likecurl --fail. Whennuxt devruns non-interactively, it suggests usingnuxt curl(#1464).nuxt info(82fe2d6),nuxt module searchandnuxt task listall gain--jsonoutput (da95c8b).📚
nuxt docsYou can now search the Nuxt documentation from your terminal (
f2dbd8c):nuxt docs "server routes"It searches the docs for the Nuxt version your project actually depends on, and opens the best match in your browser (or lets you pick, if there are several).
✨ Lots of smaller things
Typos: mistype a command or a flag and we'll suggest the one you meant, and offer to rerun it (
b841b87,07e7981,36119fe).Layers:
nuxt addcan now add layers, and detects module subpaths, sonuxt add vue-sonneraddsvue-sonner/nuxt(feat(add): support adding layers via cli #1399).pnpm catalogs:
nuxt upgradeandnuxt infounderstand pnpm catalogs (feat: support pnpm catalogs #1397).Module search shows descriptions and highlights matches (
e621b9c).Update nudges: the CLI lets you know when a newer Nuxt release is available (feat: nudge when a newer nuxt release is available #1393, feat: improve upgrade hints + add init hint/tip #1406). Set
NUXT_IGNORE_UPDATE_CHECK=1to turn this off.Dev server:
--strictPort,--open.urlfor a custom path,BROWSERsupport,r/url/qr/copyshortcuts in plain mode, a hint about--hostinside Docker or WSL, a warning when the server is reachable from the network, and StackBlitz and portless URL detection (b6ee3b9,f693d33,c1c6687,bf72c89,29558d5,40ab1f6,62a030f,a0eace5).Network failures suggest a proxy-aware retry command (feat: suggest proxy-aware retry command when network fails #1387), and first-run failures are explained before Nuxt even loads (feat(dev): explain first-run failures before nuxt loads #1444).
Builds without a server runtime:
analyze,build,devandpreviewno longer assume Nitro is present, which makes the CLI more resilient to different Nitro versions and pure Vite SPA builds (feat(analyze,build,dev,preview): support builds without a server runtime #1505, fix(analyze,build,dev,preview): server build descriptor with nitro fallback #1509).Security hardening:
cloudflareddownloads are pinned and checksum-verified, internal dev endpoints reject unknownHostheaders,GITHUB_TOKENis only sent to GitHub, and templates can't be written outside your project (3ad862d,14c268e,2a1e5b0,29e69b9).The CLI docs now live in this repository (#1465), alongside a terminal capture harness that produces the recordings you see above (#1461).
✅ Upgrading
@nuxt/cliis a dependency ofnuxt, and your project runs whichever versionnuxtdepends on. The way to get v4 is to upgrade to Nuxt 4.6, which depends on it:If you'd like to try CLI v4 before upgrading Nuxt, you can override the version
nuxtdepends on. With pnpm, inpnpm-workspace.yaml:With npm (or bun), in
package.json:{ "overrides": { "@nuxt/cli": "^4.0.0" } }With yarn, use
resolutionsinstead ofoverrides. Then reinstall, and check withnpx nuxt --version.Note
Installing
@nuxt/clidirectly, or updating a globalnuxi, isn't enough on its own. A globalnuxihands off to your project's own@nuxt/cli(v3.26 or later), so each project runs its own CLI version.If you're getting CLI v4 by upgrading to Nuxt 4.6, there should be nothing for you to change. 🎉
This is a major release only because it drops support for some older setups. These changes only matter if you override
@nuxt/clion an older version of Nuxt, or use a globalnuxiwith older projects:4360712). This matches what Nuxt 4.5 already requires, but Nuxt 3 projects on Node 20 will need a newer Node to use CLI v4.@nuxt/bridgeare no longer supported, as Nuxt 2 has been EOL for some time now (perf(nuxi)!: defer to project cli + drop nuxt <3.26 support #1410).nuxionly hands off to a project@nuxt/cliof v3.26 or later (perf(nuxi)!: defer to project cli + drop nuxt <3.26 support #1410). Every Nuxt 4 release (and Nuxt 3.18+) depends on a compatible version, so this only affects projects with an older lockfile, which will run the globalnuxi's own commands instead.nuxt inithas moved out of@nuxt/cli. We consider it an edge case to use an already-installed Nuxt to create another one, so instead just usenpm create nuxt@latest(ornpx nuxi init). This saves around 220 KB in every Nuxt project (fix(init)!: dropinitcommand from@nuxt/cli#1420).There are also a couple of other changes you'll notice, though they shouldn't need any action:
nuxt devshows the interactive UI by default in an interactive terminal. Scripts and CI are unaffected, and--no-tuiorNUXT_TUI=plainrestores the previous output.my-badinstead ofyouch(feat(dev): render dev-time errors withmy-bad#1518).👉 Changelog
compare changes
🚀 Enhancements
shift-y(feat(dev): copy everything a view shows withshift-y#1537)2d9f1a8)my-bad(feat(dev): render dev-time errors withmy-bad#1518)🔥 Performance
nuxi devbefore handing to project cli (11226da)🩹 Fixes
6a9633a).npmrcfor background update check (27ad872)ddf1c28)26d7c6e)d8fc5d0)💅 Refactors
✅ Tests
6fa982d)9e18028)🤖 CI
ef6a852)462aa51)d19f5db)pnpm/setupanddevEngines(ci: usepnpm/setupanddevEngines#1530)a2a2e0e)9f70f03)❤️ Contributors