Backend & APIs

Node 24 vs Node 26: Benchmarks, Breaking Changes and Should You Upgrade (2026)

Node 26 became Active LTS on Oct 28, 2026. I benchmarked Node 24.22.2 against 26.13.0 on the same VM and listed the breaking changes, new APIs and how to upgrade.

Node 24 vs Node 26 cover: median benchmark deltas measured on the same Linux box, Node v24.22.2 against v26.13.0
On this page
  1. Node 24 vs Node 26 at a glance
  2. What's new in Node 26
  3. Breaking changes to check before upgrading
  4. Node 24 vs Node 26 performance: benchmark setup
  5. Benchmark results
  6. How to upgrade from Node 24 to Node 26
  7. Should you upgrade to Node 26 now?

Node.js 26 became the Active LTS line on October 28, 2026 with v26.13.0 "Lithium", and Node 24 moved to Maintenance LTS on October 20. If you're deciding between Node 24 vs Node 26 for a production app, this post covers what changes, what breaks, and how the two compare on a few benchmarks I ran on the same machine tonight. The short version: Node 26 was faster at JSON serialization, async-heavy code and a plain node:http server, but slower in two places you might not expect, fetch() and inserting a million keys into a Map.

Node 24 vs Node 26 at a glance#

Node 24 "Krypton"Node 26 "Lithium"
Version testedv24.22.2v26.13.0
Status (Nov 2026)Maintenance LTS since Oct 20, 2026Active LTS since Oct 28, 2026
End of lifeApril 30, 2028April 30, 2029
V813.614.6
Undici (powers fetch)7.30.18.12.2
NODE_MODULE_VERSION137147
Bundled npm11.19.111.20.0
Temporal globalnot availableon by default

Dates come from the Node.js release schedule. Versions are what process.versions printed on each binary.

What's new in Node 26#

From the Node 26.0.0 release notes and checked on v26.13.0:

  • Temporal is enabled by default. Temporal.Now.plainDateISO('Asia/Karachi') works with no flag. On Node 24, Temporal is undefined.
  • V8 14.6 brings Map.prototype.getOrInsert() / getOrInsertComputed() (the upsert proposal) and Iterator.concat().
  • Undici 8, so the global fetch() is a different major version of the HTTP client.
  • TypeScript type stripping still works out of the box for .ts files, as it does on Node 24. --experimental-transform-types was removed, so enums and namespaces that need transformation won't run without a build step.
js
// Node 26 only
const counts = new Map();
for (const word of ['a', 'b', 'a']) {
  counts.set(word, counts.getOrInsert(word, 0) + 1);
}

const today = Temporal.Now.plainDateISO('Asia/Karachi');
console.log(today.add({ days: 30 }).toString());

const all = Iterator.concat([1, 2].values(), [3].values()).toArray(); // [1, 2, 3]

Breaking changes to check before upgrading#

These are the ones most likely to hit a typical Node.js, NestJS or Next.js codebase:

  1. Native addons must be rebuilt. NODE_MODULE_VERSION went from 137 to 147. Anything with a compiled .node file (bcrypt, sharp, better-sqlite3, canvas) needs a fresh install on Node 26. Delete node_modules in your Docker build rather than copying it from a Node 24 stage.
  2. http.ServerResponse.prototype.writeHeader() is gone. It was a legacy alias. On v26.13.0 typeof res.writeHeader is undefined. Use writeHead().
  3. The _stream_* legacy modules are removed (_stream_readable, _stream_writable and friends). Old dependencies that require('_stream_readable') will crash at import.
  4. module.register() is runtime-deprecated. Running a loader that calls it prints DEP0205 DeprecationWarning: module.register() is deprecated. Use module.registerHooks() instead. It still works, but plan to move custom loaders to registerHooks().
  5. --experimental-transform-types is removed. If you run .ts with enums directly through Node, switch to erasableSyntaxOnly style code or keep a compiler step.

A quick way to find the first two in a codebase:

bash
grep -rn "writeHeader(\|_stream_readable\|_stream_writable\|module.register(" src/ node_modules/.pnpm 2>/dev/null | head

Node 24 vs Node 26 performance: benchmark setup#

I ran these on November 3, 2026 on one Linux VM with 8 vCPUs (Intel Xeon) and 16 GB RAM. The machine was shared with other work (load average around 1.5 before starting), so treat small differences as noise. To limit that:

  • Each benchmark process was pinned to one CPU with taskset.
  • Node 24 and Node 26 runs were interleaved over 5 rounds, and I report the median of the 5 rounds.
  • The HTTP load generator (autocannon, 100 connections, 10 s) always ran on Node 24 on separate CPUs, so only the server changed.
  • The fetch() test sent 20,000 requests with 50 concurrent loops to a fixed Node 24 server, so only the client changed.

The scripts are plain .mjs files: JSON.parse/JSON.stringify on a 20,000-item document, sorting 300,000 numbers, setting 1 million string keys in a Map, a global regex replace, SHA-256 over 16 MB, allocating 2 million small class instances, and 1 million sequential awaits.

Benchmark results#

Lower is better for times, higher is better for requests per second. Median of 5 rounds.

TestNode 24.22.2Node 26.13.0Change
JSON.parse (20k items)11.79 ms10.46 ms11% faster
JSON.stringify (20k items)7.20 ms4.53 ms37% faster
Sort 300k numbers54.6 ms51.7 ms5% faster
Map.set 1M string keys396.7 ms507.5 ms28% slower
Regex replace2.06 ms1.72 ms16% faster
SHA-256 over 16 MB9.57 ms9.17 ms4% faster (within noise)
2M class instances47.8 ms35.0 ms27% faster
1M sequential await58.6 ms35.0 ms40% faster
node -e 0 startup22.3 ms20.7 ms7% faster
RSS after startup41 MB41 MBsame
node:http server, req/s73,07979,6539% more
node:http p99 latency3 ms2 mslower
fetch() client, req/s8,4427,60110% fewer

What stood out:

  • JSON.stringify and await got much cheaper. Those two show up everywhere in API servers, so the 9% higher node:http throughput is consistent with them.
  • fetch() was slower in 4 of 5 rounds. Node 26 ships Undici 8. If your service makes lots of outbound HTTP calls, measure your own workload before and after.
  • Map.set with 1 million new string keys was slower in every round. The Node 24 numbers for this test varied a lot (336 to 513 ms), so the size of the gap is uncertain, but the direction was consistent. Code that builds very large maps in a hot path is worth profiling.

These are microbenchmarks on one VM. They tell you where to look, not what your app will do.

How to upgrade from Node 24 to Node 26#

bash
# 1. Switch locally
nvm install 26 && nvm use 26     # or: fnm use 26 / volta install node@26

# 2. Reinstall so native addons rebuild against ABI 147
rm -rf node_modules && npm ci

# 3. Surface deprecations your code triggers
NODE_OPTIONS=--trace-deprecation npm test

Then update the places that pin the version:

dockerfile
# Dockerfile
FROM node:26-alpine AS deps
json
// package.json
{ "engines": { "node": ">=26" } }

Also update .nvmrc, your CI matrix and the Node version in your hosting panel. If you deploy Next.js in Docker, the multi-stage setup in my Next.js Dockerfile guide works the same with node:26-alpine as the base image.

Should you upgrade to Node 26 now?#

  • New projects: start on Node 26. It's the Active LTS line and is supported a year longer than Node 24.
  • Existing Node 24 apps: there's no rush. Node 24 gets security fixes until April 30, 2028. Upgrade when you next touch your Docker base image, after rebuilding native addons and running your tests with --trace-deprecation.
  • Services that are mostly outbound HTTP: benchmark your own fetch() traffic first, because that was the one place Node 26 was clearly slower in my test.

If you're also weighing runtimes rather than versions, I compared Bun vs Node on real workloads earlier.

Questions