Skip to content

v4.0.0 - #1482

Merged
danielroe merged 3 commits into
mainfrom
release/v4.0.0
Oct 5, 2026
Merged

v4.0.0#1482
danielroe merged 3 commits into
mainfrom
release/v4.0.0

Conversation

@github-actions

@github-actions github-actions Bot commented Aug 24, 2026 •

Copy link
Copy Markdown
Contributor

v4.0.0 is the next major release.

👀 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 dev experience: 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-bad

Dev-time errors are now rendered with my-bad, which replaces youch (#1518).

A Vue error rendered by my-bad, showing the original source frame in app/pages/broken.vue

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.

The collapsed my-bad overlay in the corner of a Nuxt error page

There's a Copy error button with a few formats, including a prompt you can hand straight to an agent:

The my-bad copy menu: copy as Markdown, copy prompt for an agent, copy structured JSON

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 your nuxt.config has 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:

my-bad showing a build error for a missing @nuxt/image module

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:

[request error] [GET] /broken

  ✖ TypeError (500): Cannot read properties of null (reading 'name')

    <nuxt-root> › <app> › <NuxtPage> › <RouterView> › <RouteProvider> › <broken>

      1 │ <script setup lang="ts">
      2 │ const user = { profile: null as null | { name: string } }
    › 3 │ const name = user.profile!.name.toUpperCase()
        │                            ^
      4 │ </script>

    at setup app/pages/broken.vue:3:28
    … 9 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 with youch.

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 dev

I am very excited about this one. In an interactive terminal, nuxt dev now 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).

nuxt dev with the interactive terminal UI

Everything is a single keypress away:

Key Action
r / shift-r Restart the dev server (optionally with a cleared cache)
o Open the app in your browser
y Copy the server URL to the clipboard
i Versions, URLs, QR code and session info
l / e Browse the log history, or jump to the last error
n Browse served requests
p Browse pages and server routes
c Clear the console without deleting history
x Delete log and request history
? Show all shortcuts
q Quit

Every 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 n view to see what it logged (2d9f1a8, #1557, #1558, #1474).

Select a request and press enter to 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, y copies the selected row and shift-y copies 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):

nuxt dev prompting to install a module from the interactive UI

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, and notify() for a notice that lands in the log history. (To use this, make sure your module has @nuxt/kit v4.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 set NUXT_TUI=plain for good) if you prefer the classic output.

We've also tried to make the dev server surface more information about what it's doing:

nuxt dev reporting which nuxt.config keys changed on restart

⚡️ 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/cli has gone from 70 packages to 31 and from 13.1 MB to 3.5 MB installed, and the global nuxi package from 6.0 MB to 0.8 MB. We've swapped out a number of dependencies for Node built-ins or smaller alternatives (picocolors for node:util styleText, semver for verkit, fuse.js for fuzzysort, magicast for oxc, ofetch for native fetch, source-map-js for Node's SourceMap), stopped bundling jiti, and kept registry lookups and the update check off the startup path (#1379, #1409, #1413, #1416, #1425, #1430, #1443).

v3.37.0 v4.0.0
@nuxt/cli install size 13.1 MB 3.5 MB -73%
@nuxt/cli dependencies 70 31 -56%
nuxi install size 6.0 MB 0.8 MB -87%
nuxt --help 108 ms 73 ms -32%
nuxi --help (global) 88 ms 49 ms -44%
nuxt dev: first paint 330 ms 50 ms 6.6x faster
nuxt dev: port bound 338 ms 104 ms 3.2x faster
nuxt dev: first page served 3.2 s 2.6 s -19%
nuxt dev: memory at rest (Linux) 630 MB 440 MB -30%
Nuxt CLI startup and install-size benchmark results

Note

This was measured on a minimal pnpm create nuxt app with Nuxt 4.5.2, Node 24.15 on arm64 Linux, median of 10 runs.

A few of the changes behind this:

🤖 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 second nuxt dev for 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):

  • If an agent starts nuxt dev while another non-interactive server is running, it takes over (loudly).
  • If the running server is one you started in a terminal, a non-interactive nuxt dev refuses and tells the agent where your server is.
  • Pass --takeover to 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=true

nuxt curl pretty-printing a JSON response

nuxt curl pretty-prints and highlights responses in a terminal, and exits with 22 on HTTP errors, just like curl --fail. When nuxt dev runs non-interactively, it suggests using nuxt curl (#1464).

nuxt preview also records its running process and supports --takeover to replace an existing preview server (#1572).

nuxt info (82fe2d6), nuxt module search and nuxt task list all gain --json output (da95c8b).

📚 nuxt docs

You 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

The CLI docs now live in this repository (#1465), alongside a terminal capture harness that produces the recordings you see above (#1461).

✅ Upgrading

@nuxt/cli is a dependency of nuxt, and your project runs whichever version nuxt depends on. The way to get v4 is to upgrade to Nuxt 4.6, which depends on it:

npx nuxt upgrade --dedupe

If you'd like to try CLI v4 before upgrading Nuxt, you can override the version nuxt depends on. With pnpm, in pnpm-workspace.yaml:

overrides:
  '@nuxt/cli': ^4.0.0

With npm (or bun), in package.json:

{
  "overrides": {
    "@nuxt/cli": "^4.0.0"
  }
}

With yarn, use resolutions instead of overrides. Then reinstall, and check with npx nuxt --version.

Note

Installing @nuxt/cli directly, or updating a global nuxi, isn't enough on its own. A global nuxi hands off to your project's own @nuxt/cli (v3.26 or later), so each project runs its own CLI version.

⚠️ Breaking changes

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/cli on an older version of Nuxt, or use a global nuxi with older projects:

  • Node.js v22.21+, v24.11+ or v26+ is now required. The CLI uses native TypeScript type stripping and Node's environment-proxy support (4360712, e4aae262). Projects on an older Node version need to upgrade before using CLI v4.
  • Nuxt 2 and @nuxt/bridge are 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).
  • A global nuxi only hands off to a project @nuxt/cli of 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 global nuxi's own commands instead.
  • nuxt init has moved out of @nuxt/cli. We consider it an edge case to use an already-installed Nuxt to create another one, so instead just use npm create nuxt@latest (or npx nuxi init). This saves around 220 KB in every Nuxt project (fix(init)!: drop init command from @nuxt/cli #1420).

There are also a couple of other changes you'll notice, though they shouldn't need any action:

  • nuxt dev shows the interactive UI by default in an interactive terminal. Scripts and CI are unaffected, and --no-tui or NUXT_TUI=plain restores the previous output.
  • Dev-time errors are rendered with my-bad instead of youch (feat(dev): render dev-time errors with my-bad #1518).

👉 Changelog

compare changes

🚀 Enhancements

🔥 Performance

🩹 Fixes

💅 Refactors

🏡 Chore

  • trigger ci (ad36d130)
  • drop tinyexec + use nightly environment (4d0d3e90)
  • count built-ins loaded on startup paths (579ee5d3)
  • compress comments (d32f8987)
  • ⚠️ require node 22.21+ and drop env proxy support detection (e4aae262)
  • remove old todos, unneeded knip entries + orphan scripts (aa3b78b1)

✅ Tests

🤖 CI

🎉 New Contributors

❤️ Contributors

@danielroe danielroe closed this Aug 24, 2026
@danielroe danielroe reopened this Aug 24, 2026
@pkg-pr-new

pkg-pr-new Bot commented Aug 24, 2026 •

Copy link
Copy Markdown
  • nuxt-cli-playground

    npm i https://pkg.pr.new/create-nuxt@1482
    
    npm i https://pkg.pr.new/nuxi@1482
    
    npm i https://pkg.pr.new/@nuxt/cli@1482
    

commit: fa153d9

@github-actions

github-actions Bot commented Aug 24, 2026 •

Copy link
Copy Markdown
Contributor Author

CLI benchmark

@nuxt/cli v4.0.0-alpha.1 (baseline) vs v4.0.0 (this PR)

Metric baseline v4.0.0-alpha.1 head v4.0.0 Delta
nuxt --version wall time (median) 67 ms 67 ms +0.7%
nuxt --help wall time (median) 144 ms 144 ms -0.2%
nuxt dev --help wall time (median) 108 ms 107 ms -0.8%
nuxt --version modules loaded 35 35 0.0%
nuxt --version built-ins loaded 27 27 0.0%
nuxt --help modules loaded 134 134 0.0%
nuxt --help built-ins loaded 87 87 0.0%
nuxt dev --help modules loaded 63 63 0.0%
nuxt dev --help built-ins loaded 87 87 0.0%
Installed node_modules 2.45 MB 2.45 MB -0.0%
Published tarball (packed) 239.6 kB 239.6 kB -0.0%
Full report

@nuxt/cli v4.0.0-alpha.1 (baseline) vs v4.0.0 (head)

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 &lt;unknown-command> (no-op) 154 ms 156 ms +1.1% 150 ms / 159 ms 151 ms / 157 ms
nuxt &lt;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.

@codspeed

codspeed Bot commented Aug 24, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 2 untouched benchmarks


Comparing release/v4.0.0 (fa153d9) with main (4d0d3e9)

Open in CodSpeed

@danielroe
danielroe marked this pull request as ready for review September 26, 2026 15:18
@danielroe
danielroe self-requested a review as a code owner September 26, 2026 15:18
@danielroe
danielroe merged commit 074ab4d into main Oct 5, 2026
24 checks passed
@danielroe
danielroe deleted the release/v4.0.0 branch October 5, 2026 18:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant