Repository navigation
v4.0.0 - #1482
v4.0.0#1482
Conversation
commit: |
CLI benchmark
Full report
|
| Setting | Value |
|---|---|
| Baseline | ref:7ec8cd27a8fcc7bcfacdb3ac4f0d24fec2381b75 (v4.0.0-alpha.1) |
| Head | local packages/nuxt-cli at 78d1f11 (v4.0.0) |
| Node | v24.21.0 |
| OS | Linux 6.17.0 (kernel 6.17.0-1022-azure) |
| CPU | AMD EPYC 7763 64-Core Processor x 4 |
| Memory | 15.6 GB |
| Load average at start | 1.21, 0.37, 0.17 |
| Run started | 2026-10-05T11:26:22.100Z |
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 |
67 ms | 67 ms | +0.7% | 66 ms / 72 ms | 65 ms / 70 ms |
nuxt --version (first output byte) |
62 ms | 63 ms | +1.2% | 61 ms / 67 ms | 61 ms / 66 ms |
nuxt --help |
144 ms | 144 ms | -0.2% | 140 ms / 148 ms | 139 ms / 147 ms |
nuxt --help (first output byte) |
139 ms | 138 ms | -0.1% | 135 ms / 142 ms | 134 ms / 142 ms |
nuxt dev --help |
108 ms | 107 ms | -0.8% | 104 ms / 112 ms | 102 ms / 110 ms |
nuxt dev --help (first output byte) |
103 ms | 102 ms | -0.7% | 99 ms / 107 ms | 98 ms / 105 ms |
nuxt <unknown-command> (no-op) |
154 ms | 156 ms | +1.1% | 150 ms / 159 ms | 151 ms / 157 ms |
nuxt <unknown-command> (no-op) (first output byte) |
148 ms | 150 ms | +1.2% | 145 ms / 153 ms | 145 ms / 151 ms |
Module load cost
Counted with a module.registerHooks load hook, compile cache disabled. Counts every JS module actually evaluated on that code path (native addons excluded). Built-ins loaded after bootstrap are counted separately, including the internal modules they load.
| 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 | baseline v4.0.0-alpha.1 built-ins | head v4.0.0 built-ins | Delta |
|---|---|---|---|---|---|---|---|---|---|
nuxt --version |
35 | 35 | 0.0% | 297.8 kB | 297.8 kB | -0.0% | 27 | 27 | 0.0% |
nuxt --help |
134 | 134 | 0.0% | 842.5 kB | 842.5 kB | -0.0% | 87 | 87 | 0.0% |
nuxt dev --help |
63 | 63 | 0.0% | 453.0 kB | 453.0 kB | -0.0% | 87 | 87 | 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 |
23 | 23 | 0.0% |
| Packages in the installed tree (unique name@version) | 39 | 39 | 0.0% |
| Unique package names | 39 | 39 | 0.0% |
| Package directories on disk (cross-check) | 32 | 32 | 0.0% |
Installed node_modules on disk |
2.45 MB | 2.45 MB | -0.0% |
| Installed files | 434 | 434 | 0.0% |
| Install wall time (warm npm cache, median of 3) | 1.31 s | 1.31 s | -0.1% |
| Published tarball (packed) | 239.6 kB | 239.6 kB | -0.0% |
| Published tarball (unpacked) | 775.2 kB | 774.9 kB | -0.0% |
| Files in tarball | 99 | 99 | 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.
77c7361 to
d294420
Compare
👀 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:
config-error-recovery-syntax-dark.webm
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/enpcx?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).Select a request and press
enterto open its timeline: middleware, plugins, hooks, data fetching, rendering and outgoing requests, followed by Vite compilation timings and the request's logs (#1582). The detail available depends on your Nuxt and Nitro versions. Inside a view,ycopies the selected row andshift-ycopies the filtered contents (#1537).Screen.Recording.2026-09-27.at.16.58.56.mov
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 away 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 kept 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 previewalso records its running process and supports--takeoverto replace an existing preview server (#1572).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/u/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, check that your Node.js version meets Nuxt 4.6's requirement (Node.js v22.21+, v24.11+ or v26+). No other migration is needed for a supported Nuxt 4 setup. 🎉
This is a major release only because it drops support for some older setups. The compatibility changes below also matter if you override
@nuxt/clion an older version of Nuxt, or use a globalnuxiwith older projects:4360712,e4aae262). Projects on an older Node version need to upgrade before using 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)2d9f1a84)my-bad(feat(dev): render dev-time errors withmy-bad#1518)🔥 Performance
f140924f)693eba49)97d50f44)0b5c730c)nuxi devbefore handing to project cli (11226dac)🩹 Fixes
.envvalues (fix(dev): let a restarted fork reload changed.envvalues #1592)4eec38b1)a5ee0fc1)1b1cd85f)package-manager-detector(fix: migrate topackage-manager-detector#1580)setGlobalProxyFromEnvto enable proxy (fix: usesetGlobalProxyFromEnvto enable proxy #1577)8babb655)54eebce3)6a9633a9).npmrcfor background update check (27ad8727)ddf1c284)26d7c6ed)d8fc5d07)💅 Refactors
50bb616a)c70a8e53)9e44f750)b405d643)2d00ab43)887a4f9f)package-manager-detector(4cba365a)🏡 Chore
ad36d130)4d0d3e90)579ee5d3)d32f8987)e4aae262)aa3b78b1)✅ Tests
325ffa20)6fa982d4)9e180285)🤖 CI
df61b399)950d9c09)ef6a8521)462aa516)d19f5dbd)pnpm/setupanddevEngines(ci: usepnpm/setupanddevEngines#1530)a2a2e0e9)9f70f032)🎉 New Contributors
❤️ Contributors