Skip to main content
Back to Blog

Node.js Just Changed Big Time: 26 Takes Over as LTS, the One-Release-a-Year Era Begins, node Runs TypeScript Natively — But Upgrading Has Pitfalls

Introduction

“We changed nothing, yet the whole CI went red this morning.”

Since October 28, plenty of teams have stepped on the same landmine: their Dockerfiles pin the floating node:lts tag, and the day Node 26 took over as LTS, their build environment jumped from 24 to 26 overnight — native addons failing to compile, legacy APIs throwing errors outright.

This was no accident: two events collided — Node 26 taking over as LTS, and the biggest overhaul of Node.js release rules in a decade.

In this article, I’ll break down this major release completely: what actually happened, how to use the new features, and which pitfalls you must avoid before upgrading. Every key fact has been verified against official sources — feel free to treat this as a checklist.


1. What Actually Happened

1. Node 26 Takes Over as LTS

First, the current version landscape (as of 2026-11-14):

VersionCodenameStatusKey Milestones
26.xLithiumActive LTSv26.0.0 released 2026-05-05; became LTS 2026-10-28; EOL 2029-04-30
24.xKryptonMaintenanceEntered maintenance 2026-10-20; EOL 2028-04-30
22.xJodMaintenanceEOL 2027-04-30
27.x—AlphaPre-releases since 2026-10-28; planned GA 2027-04-22

Node 26 (codename Lithium) officially entered Active LTS with v26.13.0 on 2026-10-28; the latest is v26.13.1 (2026-11-10, bundling npm 11.20.0). Meanwhile Node 24 entered maintenance — security fixes only, no new features.

2. The Decade-Old Even/Odd Dual-Track Scheme Is Over

The bigger news is the release-cadence overhaul the official team landed the same day. The announcement puts it bluntly: starting with 27, Node.js moves from two major versions a year to one.

The new cadence works like this:

  • Every October: cut a new release line from main, run 6 months of Alpha first (breaking changes allowed)
  • The following April: ship x.0.0 and enter Current
  • That October: promote to LTS, supported until April of the third year

In other words, every major version will now become LTS — the decade-old rule of “odd versions for experimentation, even versions for stability” is gone. The official rationale is pragmatic: odd versions saw extremely low adoption, and maintaining security patches across four or five active release lines at once became unsustainable.

Official announcement: https://nodejs.org/en/blog/announcements/evolving-the-nodejs-release-schedule

3. A Surreal Moment: For the First Time Ever, No Current

Under the old cadence, Node 27 should have shipped as Current in late October. But if you visit nodejs.org right now, there are only two download badges: Latest LTS (v26.13.1) and Latest Alpha.

Node 27 currently has only two Alpha pre-releases (latest v27.0.0-alpha.0.1.0, already bundling npm 12.x); the official release won’t land until 2027-04-22. No version is in the Current stage at this moment — a unique phenomenon of this transition period. Any claim that “Node 27 has been released” is simply wrong.


2. The Most Worthwhile New Features

All the code below was tested on the official v26.13.1 binary — zero flags required.

1. node Runs TypeScript Natively, Fully Official

This is Node 26’s headline feature. The evolution took two years:

  • v22.6.0: introduced experimentally as --experimental-strip-types
  • v23.6.0: enabled by default
  • v25.2.0 / v24.12.0: marked Stable
  • v26.0.0: even --experimental-transform-types was removed, keeping only the stable type stripping
// app.ts — runs directly on Node 26: node app.ts
interface User { id: number; name: string }
const u: User = { id: 1, name: 'fish' };
console.log(process.features.typescript); // → "strip"

Note that it does type stripping, not compilation. So syntax that requires code generation — enum, namespace with runtime code, parameter properties — throws ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX outright. The official migration advice is to use a third-party loader like tsx.

2. Temporal Date-Time API Enabled by Default

Date, the API everyone has complained about for a decade-plus, finally has an official successor. From v26, Temporal is globally available by default:

new Temporal.PlainDate(2026, 11, 14).toString(); // → "2026-11-14"
Temporal.Now.plainDateISO().toString();          // → today

Time zones, calendars, immutable objects, nanosecond precision — all pain points of Date. The community loves it, but two complaints keep coming up: the name clashes with Temporal.io’s workflow SDK, and Safari is the only browser yet to support it, so truly cross-platform use still falls one step short.

3. Permission Model Graduated, with a New Audit Mode

The --permission sandbox is no longer experimental; permissions cover eight categories: fs, net, child-process, worker, addons, wasi, ffi, and openssl-store.

Even more useful is the audit mode added in v25.8.0: report-only, no blocking — first discover which permissions your app actually needs, then tighten them:

# Step 1: audit mode — log only, don't block
node --permission-audit app.js
# The console outputs lines like: { "permission": "FileSystemRead", "resource": "target.txt" }

# Step 2: tighten based on audit results
node --permission --allow-fs-read=./ app.js
# Unauthorized access throws ERR_ACCESS_DENIED

4. V8 Upgraded to 14.6 — New Syntax Out of the Box

Node 26 ships V8 14.6.202 (corresponding to Chromium 146); several new syntax features work by default:

const m = new Map([['a', 1]]);
m.getOrInsert('b', 42);              // → 42, the Upsert proposal
Iterator.concat([1], [2]).toArray(); // → [1, 2]

Also, node:sqlite has reached Release Candidate (Stability 1.2) — no flags needed:

const { DatabaseSync } = require('node:sqlite');
const db = new DatabaseSync(':memory:');
db.exec('CREATE TABLE t(v TEXT)');
db.prepare('INSERT INTO t VALUES (?)').run('hi');
console.log(db.prepare('SELECT v FROM t').get()); // → { v: 'hi' }

The smaller tools all stabilized too: the node:test test runner is Stable, --watch is stable, and Undici bumped to 8.x.


3. Pitfalls You Must Know Before Upgrading

1. Native Addon ABI Jumps from 137 to 147

The V8 major upgrade pushed NODE_MODULE_VERSION from 137 to 147. Any native C++ addon that talks to V8 directly via the NODE_MODULE macro will throw on require under Node 26 and must be recompiled.

The good news: addons built on Node-API are unaffected in testing — which is the right way to write native addons today. One-line self-check:

node -p "process.versions.modules"   # Node 24 → 137, Node 26 → 147

Community members also warn that prebuilt binaries for native addons typically lag the LTS tag by a few weeks — don’t rush in on switchover day.

2. —experimental-transform-types Is Gone Entirely

Under v26, passing this flag fails immediately with bad option. If your project relies on it for code-generation TS syntax like enum, the upgrade breaks you. The fix is clear: switch to tsx.

3. A Batch of APIs Hard-Removed; Short GCM Tags Throw

  • http.ServerResponse.prototype.writeHeader() (the old API missing a “d”) has been removed; use writeHead()
  • All internal _stream_* modules throw ERR_UNKNOWN_BUILTIN_MODULE
  • GCM encryption caution: DEP0182 has reached End-of-Life; setAuthTag() no longer accepts authentication tags shorter than 16 bytes unless declared explicitly:
const { createDecipheriv } = require('node:crypto');
// Fix: short tags require an explicit authTagLength
const d = createDecipheriv('aes-256-gcm', key, iv, { authTagLength: 8 });
  • corepack no longer ships with Node — you must install pnpm/yarn yourself; this drew the loudest complaints on HN
  • The old exception allowing extension-less require() inside type: "module" packages is gone; resolution now follows ESM, and packages relying on the legacy behavior (e.g., yargs v17) fail loudly

📌 Docker users, take note: change FROM node:lts to a pinned exact version like FROM node:26.13.1, and let Renovate/Dependabot handle upgrades via PRs — almost every crash post from LTS switchover day traces back to floating tags.


4. What the Community Is Arguing About

The performance narrative is split. An open, reproducible community benchmark (comparing 24.15.0 / 25.9.0 / 26.2.0) shows Node 26 winning both ways on synthetic real-world apps: lowest p95 latency at 1.65ms and lowest peak memory at 273MB. But targeted microbenchmarks show losses — HTTP GET throughput is about 7% lower than 24, and string concatenation drops 32%; meanwhile integer loops run about 9.5x faster, a dividend from the V8 upgrade.

Bun is chasing hard. Bun 1.4 aligned directly against Node v26.3.0’s official test suite, adding 1517 passing tests, and its self-reported numbers show startup at 5.1ms vs 27.2ms and dependency install at 1.41s vs 18.1s — though the comparison methodology is Bun’s own, so take it as entertainment.

The ecosystem is catching up fast: Vercel has made 26.x the default runtime for new projects; Vite 8 and Next.js 16 still set their minimum at Node 20/22, so you can keep working without upgrading to 26.

The fundamentals keep growing: Node.js downloads hit 4.61 billion in the first seven months of 2026, already surpassing all of 2025.


Conclusion

This major release in one sentence: Node 26 takes over as LTS, and Node.js enters the one-release-a-year era.

Your action list:

  1. Production: pin exact version numbers — never use the floating node:lts tag
  2. Before upgrading to Node 26: run process.versions.modules to audit native addons, and fix short GCM tag code first
  3. Local development: install Node 26 now — running TypeScript directly with node app.ts is an experience worth picking up immediately
  4. April 22 next year: Node 27.0.0 ships — the first product of the new one-release-a-year cadence

The release rules changed, but the direction is right — fewer maintenance lines, longer support windows, more predictable expectations. A net win.

Follow FishTech Notes on WeChat to share your experience!