What Is New in Node.js 21?
| Category | Change |
|---|---|
| New Features | Stable WebSocket client (no flag needed) |
| New Features | V8 11.8 -- includes Array grouping, Promise.withResolvers |
| New Features | Built-in fetch() now stable |
| Improvements | Test runner: --test-reporter and --test-reporter-destination flags |
| Improvements | node:fs glob() added (experimental) |
| Improvements | navigator.hardwareConcurrency exposed in global scope |
| Security | OpenSSL 3.1.4 bundled |
| Deprecated | Experimental fetch warnings removed (fetch is now stable) |
Stable WebSocket Client -- No More Undici Import
Node.js 21 ships a built-in WebSocket global that works without any flags or third-party packages. It follows the WhatWG WebSocket standard and uses Undici under the hood.
const ws = new WebSocket('wss://echo.example.com');
ws.addEventListener('open', () => ws.send('hello'));
ws.addEventListener('message', (e) => console.log(e.data));
This is significant for server-to-server communication, live data feeds, and any tooling that previously needed ws or undici's WebSocket implementation as a dependency.
New JavaScript Features via V8 11.8
Array grouping (Object.groupBy and Map.groupBy) ships natively -- a long-requested feature for grouping collections by a key without a reducer. Promise.withResolvers() simplifies creating manually-resolved promises.
// Object.groupBy
const items = [{type: 'a', v: 1}, {type: 'b', v: 2}, {type: 'a', v: 3}];
const grouped = Object.groupBy(items, (i) => i.type);
// { a: [...], b: [...] }
// Promise.withResolvers
const { promise, resolve, reject } = Promise.withResolvers();
setTimeout(() => resolve('done'), 1000);
Test Runner Improvements
The built-in test runner now supports --test-reporter and --test-reporter-destination flags. You can pipe test output to spec, tap, junit, or dot formatters and redirect each to a file or stdout independently.
This makes the built-in runner a more complete replacement for Jest/Mocha in CI pipelines that require structured test output (e.g., JUnit XML for GitHub Actions or Jenkins).
Web API Globals Expanded
navigator.hardwareConcurrency is now available in the global scope, matching browser behavior. Scripts that dynamically size worker pools based on CPU count no longer need to os.cpus().length workarounds when targeting both browser and Node environments.
FAQ
Is the built-in WebSocket client compatible with the browser WebSocket API?
Yes, it implements the WhatWG WebSocket standard. Code written for browser WebSocket should work in Node.js 21 without modification.
Can I use Object.groupBy in production with Node.js 21?
Yes, it's part of the ECMAScript 2024 spec and natively available in V8 11.8. No polyfill needed.
What reporters does the built-in test runner support?
spec (human-readable), tap, dot, and junit. You can combine multiple reporters with multiple --test-reporter and --test-reporter-destination pairs.
Is Node.js 21 an LTS release?
No. Node.js 21 is a Current release. The LTS line from this generation is Node.js 22. Use 21 for feature evaluation, not long-term production deployments.
Does fetch() in Node.js 21 support streaming response bodies?
Yes. You can read response bodies as streams via response.body (a ReadableStream), and pipe them using the WHATWG Streams API.