The landscape of server-side JavaScript has stabilized significantly over the last year. As we settle into 2026, Node.js 22 LTS (Long Term Support) has solidified its place as the production standard for enterprise and high-scale applications.
While previous versions focused on keeping up with modern ECMAScript standards, Node.js 22 marked a pivotal shift: reducing the dependency on external tooling.
If your package.json is still bloated with libraries for globbing patterns, parsing arguments, or handling simple WebSockets, it’s time to refactor. Node.js 22 allows us to write leaner, faster code by “using the platform.”
In this guide, we will explore the top features of Node.js 22 that every senior developer needs to leverage in production environments.
Prerequisites and Setup #
To follow along with the code examples, ensure you are running the active LTS version.
- Check Version:
node -v # Output should be v22.x.x or higher - Environment: A standard text editor (VS Code recommended) and a terminal.
- Project Init:
mkdir node22-features cd node22-features npm init -y
Let’s dive into the features that are reshaping our workflows.
1. Native WebSocket Client #
For over a decade, the first thing a developer did when needing real-time communication was npm install ws or npm install socket.io-client.
Node.js 22 introduced a browser-compatible implementation of the WebSocket global. This is huge for isomorphism—you can now share connection logic between your frontend (React/Vue) and your Node.js backend services without conditional imports or polyfills.
How to use it #
You no longer need to import anything. The WebSocket object is available globally.
// client.mjs
// No imports needed!
const socket = new WebSocket('wss://echo.websocket.org');
socket.addEventListener('open', () => {
console.log('✅ Connected to server');
socket.send('Hello from Node.js 22 Native Client!');
});
socket.addEventListener('message', (event) => {
console.log(`📩 Received: ${event.data}`);
// Close connection after receiving message to end script
socket.close();
});
socket.addEventListener('close', () => {
console.log('❌ Disconnected');
});
socket.addEventListener('error', (err) => {
console.error('⚠️ Error:', err.message);
});Why this matters: It reduces the security surface area of your application. Supply chain attacks often target popular dependencies. By removing a dependency for simple WebSocket clients, you reduce your risk profile.
2. require() Support for ES Modules
#
The war between CommonJS (require) and ES Modules (import) has caused more headaches than perhaps any other topic in the Node ecosystem.
Historically, if a library was pure ESM (using export default), you couldn’t require() it in a CommonJS application. You had to use dynamic import(), which forced your synchronous code to become asynchronous.
Node.js 22 introduced the ability to require() synchronous ESM graphs. This feature bridges the gap, allowing legacy codebases to consume modern libraries without a total rewrite.
The Implementation #
Note: Depending on your specific minor version of Node 22, this might still require the --experimental-require-module flag, though it is becoming standard behavior.
Create an ESM module:
// math-utils.mjs (Note the extension)
export function add(a, b) {
return a + b;
}
export default {
description: "A simple math module"
};Consume it in a CommonJS file:
// app.js
// We can now require the ESM file directly!
const math = require('./math-utils.mjs');
console.log(math.add(10, 5)); // Outputs: 15
console.log(math.default.description); // Outputs: A simple math module
Key Constraint: The ESM module being required must represent a synchronous graph. If the module uses top-level await, it cannot be required and must be imported.
3. Native File System Globbing #
Before Node.js 22, tasks like “find all .js files in the src folder” required libraries like glob, fast-glob, or globby.
Node.js 22 added fs.glob and fs.globSync. This brings pattern matching directly into the core fs module.
Code Example #
Here is a script that finds all JSON files in your project directory.
// glob-search.mjs
import fs from 'node:fs';
// 1. Asynchronous Iterator (Memory Efficient)
console.log('--- Async Search ---');
const matches = fs.glob('**/*.json');
for await (const file of matches) {
console.log(`Found: ${file}`);
}
// 2. Synchronous (Blocking, good for build scripts)
console.log('\n--- Sync Search ---');
const files = fs.globSync('package*.json');
console.log(files);Performance Note: The native implementation is highly optimized and handles streaming efficiently, preventing memory spikes when searching massive directories.
4. The Maglev Compiler & V8 Updates #
Node.js 22 ships with V8 version 12.4. One of the most significant performance boosts comes from the Maglev compiler, which is now enabled by default on supported architectures.
To understand why this matters, look at the compilation pipeline below. Maglev sits between the fast-startup interpreter (Ignition) and the top-tier optimizing compiler (Turbofan).
What does this mean for you?
- Faster Warm-up: Your CLI tools and short-lived serverless functions start faster.
- Better Peak Performance: The transition from “interpreted” to “optimized” is smoother, reducing JIT (Just-In-Time) compilation stutter.
You don’t need to write code to use this; you get it for free by upgrading to Node 22.
5. node --run (Script Runner)
#
We are all used to npm run start or yarn test. However, npm adds a noticeable overhead just to spawn a child process for the node runtime.
Node.js 22 enhanced node --run, a native script runner built directly into the binary. It reads the scripts section of your package.json and executes the command, bypassing the npm CLI logic entirely.
Performance Comparison #
| Feature | npm run <script> |
node --run <script> |
Benefit |
|---|---|---|---|
| Startup Speed | ~200-300ms overhead | < 50ms overhead | Much faster CLI responsiveness |
| Process Tree | Spawns npm -> spawns shell -> spawns node | Spawns shell -> spawns node | Cleaner signal handling (Ctrl+C) |
| Path resolution | Standard npm logic | Native implementation | Less complexity |
Usage #
If your package.json looks like this:
{
"scripts": {
"test": "node test-suite.js"
}
}Run it with:
node --run testThis is particularly noticeable in CI/CD pipelines where you might run hundreds of scripts; the milliseconds add up to minutes.
Best Practices and Pitfalls #
While these features are powerful, transitioning a production codebase requires caution.
- WebSocket Server vs. Client: Node.js 22 provides a native Client. It does not provide a full-blown native WebSocket Server implementation that replaces
wsoruWebSockets.jsfor high-concurrency backend serving. Stick towsfor creating servers. - Globbing Limitations: The native
fs.globis excellent, but if you rely on highly specific, non-standard glob features provided by libraries likemicromatch, verify that the native standard covers your patterns. require(esm)Edge Cases: While helpful, avoid mixing ESM and CJS if possible. Userequire(esm)only as a bridge for legacy maintenance. For new projects, stick to 100% ESM (type: "module"in package.json).
Conclusion #
Node.js 22 LTS is not just an incremental update; it is a “batteries-included” release that invites us to delete code. By leveraging native WebSockets, the node --run command, and internal globbing, we can reduce our dependency trees and improve application security and performance.
Action Plan:
- Audit your
package.json. Can you removeglob,rimraf, orsocket.io-client? - Update your CI pipelines to use
node --runfor script execution. - Profile your application to see the “free” performance gains from the V8 Maglev compiler.
The future of Node.js is leaner and faster. Make sure your applications are ready for it.
Found this guide helpful? Check out our article on “Migrating Large Monorepos to ESM” or subscribe to the Node DevPro newsletter for more advanced engineering tips.