Skip to content

Runtime

WebJs runs on Node 24+ or Bun. The same app source runs on either; the framework picks a runtime-neutral path internally and only the listener shell, the type stripper, and a few built-ins differ. Deno is a planned target (the listener seam is already runtime-neutral), not yet supported. This page is the single reference for the per-runtime commands and differences; other pages link here rather than repeat them.

Related reading:

Node vs Bun at a glance

AreaNode 24+BunDeno
Installnpm installbun install (required, like Node)planned
Runnpm run dev / npm run startbun run dev / bun run startplanned
Listenernode:http shellnative Bun.serve (about 1.9x req/s on the listening path)planned
TypeScript strippingbuilt-in module.stripTypeScriptTypesamaroplanned
SQLite driverbuilt-in node:sqlitebuilt-in bun:sqliteplanned
Hot reloadnode --watchbun --hotplanned
WebSocketthe ws librarynative Bun.serve (bridged to the same API)planned
103 Early Hintsyesno (Bun.serve has no informational-response API)planned
Dev edit to a page / layoutfull reload (the node --watch restart replaces the process)refreshes in place, no reloadplanned

The in-place dev refresh needs the server process to survive the edit, which is the whole of that last row. A page or layout never hydrates, so a freshly rendered page is the complete truth for it and the client router can swap it in without a reload, keeping your scroll position and the hydrated state of components outside the changed region. The server classifies the changed file and puts the verdict on the live-reload event, so it needs a process that is still alive to do the classifying.

Bun's bun --hot invalidates modules in place without restarting, so it gets the refresh. On Node, node --watch restarts the process on a change under app, components, modules, lib, or actions, or to a root middleware file, and a fresh process holds no record of what changed, so those edits are a full reload. Two Node cases still refresh in place: an edit outside that watched set (db/schema.server.ts, a webjs.dev.watch content directory), and npm run dev -- --no-hot, which keeps the server in one process on either runtime. A component edit is a full reload everywhere by design, because customElements.define is once-per-tag and swapping fresh markup onto the old class would be worse than the reload.

Either way the .ts stripping is position-preserving with no sourcemap, and the bytes the browser fetches are identical. The 103 Early Hints gap only costs a small first-load latency edge where your edge forwards 103, never correctness (the modulepreload hints still ship in the document head).

Node (the default)

Scaffold with webjs create my-app (Node is the default runtime). Then:

npm install
npm run dev      # or: npm run start

Node 24+ is required: the built-in TypeScript stripper (module.stripTypeScriptTypes, stable from Node 24) and recursive fs.watch need it. The CLI's assertNodeVersion() preflight enforces the floor.

Bun

Scaffold with webjs create my-app --runtime bun, or bun create webjs my-app (the runtime is auto-detected from the invoking package manager). Then:

bun install
bun run dev      # or: bun run start

A Bun app installs with bun install (like Node), then runs on Bun: its dev / start / db scripts force bun --bun, which overrides the webjs bin's Node shebang so the server runs on Bun, where it selects the native Bun.serve listener and strips types via amaro. The dependencies resolve from node_modules, the same as Node.

The install also gives editor type intelligence (the editor reads the .d.ts files in node_modules).

Install model and reproducibility

A Bun app commits a bun.lock (the Bun analog of package-lock.json) for reproducible, offline installs. The scaffold's Bun Dockerfile runs bun install and serves on Bun via CMD ["bun", "--bun", "run", "start"].

Future runtimes

The server's listener selection is a runtime-neutral seam: startServer chooses the Bun.serve shell on Bun and the node:http shell on Node through the same seam, which is designed to also host a Deno.serve or an embedded adapter later. When Deno support lands it will appear here. Edge runtimes with no filesystem are a separate, later target.