What Is New in Node.js 26
| Category | Highlights |
|---|---|
| New Features | Temporal API enabled by default; experimental node:ffi module; crypto.randomUUIDv7(); fs.stat() signal option; http.IncomingMessage req.signal; http writeInformation() for arbitrary 1xx codes; fs.Stats / BigIntStats Temporal.Instant support; test_runner tag filtering and order randomization; node inspect runtime expression probes; util.styleText() hex color support; SQLite serialize/deserialize |
| Improvements | V8 updated to 14.6 (Chromium 146) with Map upsert and Iterator.concat(); Undici updated to 8.3.0; stream.compose() marked stable; QUIC internal implementation completed with ALPN alerts and permission model support; crypto raw key format support; JWK support for ML-KEM and SLH-DSA; diagnostics_channel BoundedChannel; stream duplexPair destruction propagation; SQLite Percentile extension enabled; assert printf-style error messages; util.inspect() marks proxied objects |
| Breaking Changes | Legacy _stream_* internal modules removed (end-of-life); http.Server.prototype.writeHeader() removed; --experimental-transform-types flag removed; module.register() runtime-deprecated; DEP0182 crypto API moved to end-of-life; localStorage returns undefined without a file; NODE_MODULE_VERSION bumped to 147; GCC minimum bumped to 13.2; Python 3.9 build support dropped; extensionless CJS exception removed for type:module packages |
| Deprecations | DEP0203 and DEP0204 crypto APIs runtime-deprecated; DEP0201 stream API promoted to runtime deprecation; module.register() runtime-deprecated; Hmac.digest() documentation-only deprecation (DEP0206) |
What Is New in Node.js 26?
Node.js 26 is a major release that ships the Temporal API as a first-class runtime feature, advances the V8 engine to version 14.6 (Chromium 146), introduces an experimental FFI module, and cleans up a large set of long-deprecated legacy internals. It will enter LTS in October, making this the right moment to start evaluating upgrade impact in your projects.
Is the Temporal API Finally Ready for Production in Node.js 26?
Yes -- the Temporal API is now enabled by default in Node.js 26 and no flag is required. Temporal is the
long-awaited modern replacement for the Date object: it is immutable, has first-class timezone support,
and handles calendar arithmetic correctly. If you have been reaching for libraries like date-fns or
Luxon solely to compensate for Date quirks, Temporal is worth a serious look.
In practice, Temporal.Instant, Temporal.ZonedDateTime, and
Temporal.PlainDate cover the vast majority of server-side date-handling tasks. Node.js 26.2.0 added
a deeper integration point: fs.Stats and BigIntStats now expose timing fields as
Temporal.Instant values, letting you write stat.mtimeInstant without a conversion step.
// Temporal in Node.js 26 -- no flag needed
const now = Temporal.Now.zonedDateTimeISO();
console.log(now.toString());
// 2026-06-07T10:30:00+07:00[Asia/Ho_Chi_Minh]
// fs.Stats Temporal integration (26.2.0+)
import { stat } from 'node:fs/promises';
const s = await stat('./package.json');
console.log(s.mtimeInstant.toString());
// 2026-06-01T04:22:10.123456789Z
Watch out for: Temporal requires Rust to be present in the build toolchain. When building Node.js from source,
make sure cargo and rustc are available. Prebuilt binaries ship with Temporal enabled
out of the box.
What Does V8 14.6 Bring to Node.js 26?
V8 14.6 (Chromium 146) is the JavaScript engine powering Node.js 26. This version ships two newly graduated TC39 proposals directly into the runtime without any flag.
-
Map / WeakMap upsert (Stage 4):
Map.prototype.getOrInsert(key, defaultValue)andMap.prototype.getOrInsertComputed(key, callbackFn)eliminate the commonif (!map.has(k)) map.set(k, ...)boilerplate. This matters if you build caches, memoization layers, or grouping logic using plain Maps. -
Iterator sequencing:
Iterator.concat(...iterables)lazily chains any number of iterables into a single iterator. No array spread needed -- memory stays flat.
// Map upsert
const cache = new Map();
cache.getOrInsert('key', 'default'); // returns 'default', inserts it
cache.getOrInsertComputed('k2', () => expensiveCompute());
// Iterator.concat
const combined = Iterator.concat([1, 2], [3, 4], [5]);
console.log([...combined]); // [1, 2, 3, 4, 5]
The NODE_MODULE_VERSION jumped to 147, meaning any native addon compiled against an earlier Node.js version
must be recompiled. Most teams using node-gyp or prebuild pipelines should account
for a rebuild step in their upgrade checklist.
How Does the New node:ffi Module Work in Node.js 26?
Node.js 26.1.0 ships an experimental node:ffi module that lets JavaScript call native shared libraries
directly -- without writing a native addon or using a third-party package like ffi-napi. It is gated
behind the --experimental-ffi flag and, when the Permission Model is active, also requires
--allow-ffi.
// node --experimental-ffi app.mjs
import { DynamicLibrary, FFIType } from 'node:ffi';
const lib = new DynamicLibrary('libc.so.6');
const strlen = lib.get('strlen', FFIType.u64, [FFIType.pointer]);
const buf = Buffer.from('hello\0');
console.log(strlen(buf)); // 5n
This API is inherently unsafe. Passing an incorrect pointer, a mismatched type signature, or accessing memory after
it has been freed can crash the process silently or corrupt heap state. The team added
Symbol.dispose support to DynamicLibrary so you can use using declarations
to ensure the library is unloaded deterministically. Most teams should treat this as a lower-level escape hatch
for integrating system libraries, not a replacement for well-tested native addons in production-critical paths.
What Breaking Changes and Removals Should You Watch for in Node.js 26?
Node.js 26 finalizes the removal of several APIs that had been deprecated for multiple release cycles. The upgrade impact is low for greenfield code, but legacy codebases may need targeted edits.
-
_stream_* legacy internal modules removed: The internal
_stream_wrap,_stream_readable,_stream_writable,_stream_duplex,_stream_transform, and_stream_passthroughmodules are gone. Code thatrequire()'d these directly (even through a transitive dependency) will throw. Migrate to the publicnode:streamAPI. -
http.Server.prototype.writeHeader() removed: This was an old alias for
writeHead(). Do a global find-and-replace; no logic change is needed. -
--experimental-transform-types removed: TypeScript stripping is now stable in Node.js.
The experimental flag is gone -- remove it from your
package.jsonscripts. -
module.register() runtime-deprecated: The hook-based custom loader API introduced as an
experimental replacement for
--loaderis now flagged for replacement. Watch for the successor API. -
localStorage returns undefined without a file: Previously this could throw. If any code
conditionally relies on
typeof localStorage, the behavior has changed. -
Extensionless CJS exception removed for type:module packages: Packages with
"type": "module"can no longer load extensionless CJS files. Add explicit.cjsextensions where needed.
Build environment changes also matter: the minimum GCC version is now 13.2, Python 3.9 is no longer supported for building Node.js from source, and the minimum Windows SDK is now version 11.
What Improvements Landed in Node.js 26 for HTTP, Crypto, and Testing?
Beyond the headline features, Node.js 26 delivers a focused set of quality-of-life improvements across the HTTP, crypto, and test runner subsystems that teams will benefit from in day-to-day development.
HTTP: http.IncomingMessage now exposes a req.signal property
(an AbortSignal) so you can cleanly cancel in-flight request handling using standard Web API conventions.
Node.js 26.2.0 added res.writeInformation(statusCode, headers) for sending arbitrary 1xx
informational responses (think 103 Early Hints without workarounds). The ClientRequest options
merge was also hardened to prevent prototype pollution.
Crypto: The biggest addition is crypto.randomUUIDv7() for generating
time-ordered UUIDs. Compared to UUIDv4, v7 IDs sort lexicographically and are friendlier to B-tree indexes in
databases -- a common pain point when using random UUIDs as primary keys. The crypto module also gained raw key
format support in KeyObject APIs and JWK support for post-quantum key types (ML-KEM, SLH-DSA).
import { randomUUIDv7 } from 'node:crypto';
// Time-ordered, database-friendly UUIDs
const id = randomUUIDv7();
console.log(id); // e.g. 01970000-0000-7xxx-xxxx-xxxxxxxxxxxx
Test runner: The built-in test runner received several useful additions in v26.1.0 and
v26.2.0: test order randomization via --test-random-order, tag-based filtering with
--test-name-tag, mock-timer support for AbortSignal.timeout, and OTel-compatible
diagnostics channel publishing. If your team has been hesitant to adopt the native test runner, these
additions close several gaps.
FAQ -- Common Questions about Node.js 26
Does upgrading to Node.js 26 break existing native addons?
Yes, native addons must be recompiled because the NODE_MODULE_VERSION was bumped to 147. Any prebuilt binary for an earlier Node.js version will not load. Run npm rebuild or re-download prebuilds for packages like bcrypt, sharp, or canvas before deploying.
Is the Temporal API stable enough to use in production in Node.js 26?
Temporal is enabled by default and its implementation tracks the TC39 specification closely. It is not marked as experimental in Node.js 26, but the underlying spec still has a small number of open normative issues. For new greenfield date logic it is a solid choice; for critical financial or calendar calculations in existing codebases, consider a phased rollout alongside thorough test coverage.
How do I enable the node:ffi module?
Pass the --experimental-ffi flag when starting Node.js, for example: node --experimental-ffi app.mjs. If you are using the Permission Model you must also pass --allow-ffi to permit FFI calls. Because the module is inherently unsafe, it is not enabled in production server environments without explicit opt-in.
What replaces the removed _stream_* internal modules?
Use the public node:stream module. For example, replace any require('_stream_readable') with import { Readable } from 'node:stream'. The public API has been equivalent for many years; the internal modules were always a private implementation detail that leaked into userland code.
How does crypto.randomUUIDv7() differ from randomUUID()?
randomUUID() generates a version 4 UUID which is entirely random and has no sortable structure. randomUUIDv7() embeds a millisecond-precision timestamp in the high bits, making generated IDs monotonically increasing within the same millisecond and fully sortable. This is significant for database primary keys because sorted inserts avoid B-tree page splits and improve index locality. Note that the clock used is not guaranteed to be monotonic across process restarts.
Is module.register() being removed in Node.js 26?
It is runtime-deprecated in Node.js 26, meaning it will emit a deprecation warning at runtime but is not yet removed. The Node.js team is working on a successor API. Existing custom loader setups will continue to function in this release cycle, but you should plan a migration before the eventual removal in a future major version.