Node.js

Detected from package.json. npm, yarn, or pnpm from the lockfile.

See Buildpacksfor how detection and version policy work. A repo that also looks like a static frontend is built here, then served by the staticbuildpack.

Detects

package.json in the base directory.

Versions

Default is Node.js 24. Published lines as of September 2026: 22, 24, 25, 26. You get the latest patch of the requested minor.

Pin, in this order:

  • .node-version
  • .nvmrc
  • engines.node in package.json (a range such as ^22 picks the highest matching published version)

A leading v is stripped.

Install and build

The lockfile picks the package manager: pnpm-lock.yaml, then yarn.lock, otherwise npm. A lockfile is required (npm ci or a frozen install). The build fails without one.

If package.json has a build script, that runs. A heroku-postbuild script runs after that, if present. Dev dependencies are pruned for the run image, except Next.js apps that start with a TypeScript next.config.* (those keep typescript at launch).

Yarn and pnpm go through Corepack. Launch sets NODE_ENV=production and puts node_modules/.bin on PATH.

Start command

First match wins:

  1. The start script in package.json
  2. A known SSR framework default (see below)
  3. node server.js, index.js, or app.js if that file exists
  4. node .

Override with a Procfile. Listen on PORT.

Frameworks

  • Next.js: next start, unless output: 'export', which is a static site (out/).
  • Nuxt: node .output/server/index.mjs
  • SvelteKit: static with adapter-static (build/), or node build with adapter-node
  • Remix / React Router: npx remix-serve ./build/server/index.js
  • Astro: static dist/, or node ./dist/server/entry.mjs when output is server or hybrid
  • Angular, Gatsby, Create React App, Eleventy, Vite: treated as static output

PHP + Node (Laravel with Vite) and Ruby + Node (Rails with JS) are polyglot groups. See PHPand Ruby.