Node.js
How Anvil runs Next.js, Nuxt, Vite and other Node.js projects, installs Node.js versions and package managers, and picks a version per project.
What Anvil detects
| Type | Detected by | Started with |
|---|---|---|
| Next.js | next in package.json | npm run dev -- --hostname 127.0.0.1 |
| Nuxt | nuxt in package.json | npm run dev |
| Vite | vite or vite-plus in package.json | npm run dev -- --port $PORT --strictPort --host 127.0.0.1 |
| Node.js | package.json | npm run dev, or npm run start without a dev script |
PHP, Rust, Go, .NET and Django projects are recognized first, as are FastAPI
and Flask projects with a main.py, app.py, wsgi.py or app/main.py. A
Laravel application with a package.json stays Laravel; see the
detection table.
Anvil starts the dev server on a private port passed in $PORT and proxies
<name>.test (and <name>.localhost) to it. Vite gets the port on its command
line, Next.js reads $PORT; other servers must listen on $PORT, or set their
fixed port: anvil app port myapp 3000, or Port in the Settings tab.
Installing Node.js
Settings › Languages › Node.js › Install, or:
anvil runtime list
anvil runtime install node 24Every major from 18 up that nodejs.org lists. Anvil installs the newest
release of the major, checked against its SHASUMS256.txt, into
~/.anvil/runtimes/node/<major>.
Package managers
The Package managers list in Settings › Languages › Node.js shows which npm, pnpm, yarn and bun your applications and your terminal get, and from where. Install adds a missing one:
- pnpm and yarn: installed with the newest Anvil Node.js's npm into
~/.anvil/runtimes/pm(needs a Node.js installed). - bun: official release, checked against its SHA-256, into
~/.anvil/runtimes/bun.
Processes Anvil runs, and scripts run via Anvil's npm/npx, get Anvil's
copies first. Typed in a terminal, pnpm, yarn and bun are whatever your
own PATH finds.
Choosing the version
There is no default version. Your pin wins: anvil app runtime myapp 22, or
Node.js version in the Settings tab. Otherwise the first of these that
names an installed version:
-
nodein.anvil.toml(a major):node = "22"If it isn't installed, the Overview offers to install it.
-
.nvmrc, then.node-version. Only the major counts (v22.11.0and22both mean 22);lts/ironmatches nothing. -
The newest installed version.
anvil app runtime myapp without a version goes back to automatic.
The pin lives in Anvil's settings and applies to the dev server and to
node/npm/npx in a terminal inside the folder.
Dev processes and one-off scripts ignore
it and use the list above; to give them a version, set it in .anvil.toml or
.nvmrc.
Running an application
Before a command that runs a package script or binary (npm run, pnpm,
yarn, bun run, npx and the like), Anvil installs dependencies if
node_modules is missing:
| Lock file | Install command |
|---|---|
pnpm-lock.yaml | pnpm install --config.strict-dep-builds=false |
yarn.lock | yarn install |
bun.lockb or bun.lock | bun install |
| none | npm install |
pnpm and yarn come through Corepack when not on the application's PATH.
Output goes to the application's log; a failed install stops the start.
The dev server doesn't start on its own: Start in the Dev tab, or
anvil app start myapp while the desktop app is open (this also starts dev
processes marked Dev start). To start it with Anvil, turn on Start with
Anvil in Settings or run anvil app autostart myapp true (off by default;
with the CLI alone, restart anvil start). It runs until stopped or Anvil
quits; while stopped, <name>.test shows a "not running" page.
Start command and port: Start command and Port in Settings, or:
anvil app command myapp 'pnpm dev --port $PORT'
anvil app port myapp 41500
anvil app command myapp # default command
anvil app port myapp # automatic portThe automatic port is derived from the name, in 41000–44999, moving up on collision.
Dev server environment:
| Variable | Value |
|---|---|
PORT | The application's private port |
HOST, HOSTNAME | 127.0.0.1 |
__VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS | .test (the domain suffix) |
npm_config_cache | ~/.anvil/cache/npm |
npm_package_config_node_gyp_devdir | ~/.anvil/cache/node-gyp |
COREPACK_HOME | ~/.anvil/cache/corepack |
XDG_CACHE_HOME, XDG_STATE_HOME | ~/.anvil/cache/xdg and ~/.anvil/cache/xdg-state, unless you set them |
NEXT_TELEMETRY_DISABLED, NUXT_TELEMETRY_DISABLED | 1 |
PATH | The chosen Node.js, then Anvil's pnpm, yarn and bun, then the PATH Anvil started with and your login shell's |
Next.js addresses
Next.js dev is only interactive at localhost and at allowedDevOrigins, so
Anvil opens <name>.localhost until the config lists the domain suffix. Use
<name>.test in the Overview adds allowedDevOrigins: ['**.test'] to
next.config (or creates next.config.mjs). Anvil edits only a config with
one plain top-level object; otherwise it shows the line to add. The dev server
listens on 127.0.0.1 only.
In your terminal
Setup puts node, npm and npx
on your PATH, running Anvil's Node.js:
- Inside a Node.js application's folder: the application's version, pin included.
- Elsewhere: the version
.anvil.toml,.nvmrcor.node-versionnames, else the newest. Inside another application (e.g. Laravel) these files are read from its folder; outside any application, from the current folder.
They get the same cache variables and PATH as applications. anvil which node prints which node runs in the current folder.
Caches and files
Node.js tool caches go to ~/.anvil/cache: npm, node-gyp headers, Corepack,
and tools following XDG_CACHE_HOME/XDG_STATE_HOME (giget, yarn, pnpm's
state). ~/.npm is untouched. An existing shared store, like pnpm's in
~/Library/pnpm, stays put; moving it would make pnpm reject existing
node_modules.
In the project, Anvil writes only node_modules (via the install above) and
next.config (via Use <name>.test). node_modules and .next show as
rebuildable in Storage.
New applications
anvil new next shop --ts --tailwind --pm pnpm --node 24
anvil new nuxt docs
anvil new vite ui --framework vue --no-ts
anvil new node apiA Next.js project with the CLI alone:
anvil runtime install node 24, if needed.anvil new next shop. Created in your first parked folder (or--in);anvil startneedn't be running.anvil app autostart shop true.- Run
anvil start, or restart it (Ctrl-C) if running. - Open
http://shop.localhost. Forshop.test, addallowedDevOrigins: ['**.test']tonext.config(or Useshop.testin the app).
Or New… (⌘N) in the app, under JavaScript.
| Template | Created with | Options |
|---|---|---|
next | create-next-app@latest with the App Router | --ts/--no-ts, --tailwind/--no-tailwind, --pm npm|pnpm|bun, --node |
nuxt | nuxi@latest init with the minimal template | --pm, --node |
vite | create-vite@latest | --framework react|vue|svelte|vanilla, --ts/--no-ts, --pm, --node |
node | a package.json and a server.js that listens on $PORT (dev runs node --watch server.js) | --node |
--node takes an installed major; newest otherwise. Generators run through
Anvil's npx, then dependencies install with the chosen package manager. All
templates take --git/--no-git and --https/--no-https; see
New applications.
Differences from production
- The dev server runs, not a build. Next.js and Vite bind 127.0.0.1; others
get
HOST/HOSTNAME= 127.0.0.1, which most follow. - Next.js and Nuxt telemetry is off.
- node-gyp takes Node.js headers from
~/.anvil/cache/node-gyp.
Removing a version
Hover the version in Settings › Languages › Node.js and click
Uninstall, or anvil runtime uninstall node 22. This deletes
~/.anvil/runtimes/node/22; unpinned applications pick again on next start.
Anvil refuses while applications are pinned to it ("web uses Node.js 22 —
switch it to another version first"); Settings then shows Pinned by with
their names instead of Uninstall. Unpin: set Node.js version to
Automatic or another version, or run anvil app runtime web without a
version (with only one version installed the field is disabled; use the
command). A version only .anvil.toml, .nvmrc or .node-version asks for
doesn't block. pnpm and yarn installed from Settings stay; remove them with
Uninstall in the Package managers list.