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.nvmrcengines.nodeinpackage.json(a range such as^22picks 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:
- The
startscript inpackage.json - A known SSR framework default (see below)
node server.js,index.js, orapp.jsif that file existsnode .
Override with a Procfile. Listen on PORT.
Frameworks
- Next.js:
next start, unlessoutput: 'export', which is a static site (out/). - Nuxt:
node .output/server/index.mjs - SvelteKit: static with
adapter-static(build/), ornode buildwithadapter-node - Remix / React Router:
npx remix-serve ./build/server/index.js - Astro: static
dist/, ornode ./dist/server/entry.mjswhen output isserverorhybrid - 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.