Introduction
When I started working at Lingo.dev, I noticed that some our dependencies were redundant and could be replaced with native Node.js APIs.
This made me wonder what other packages might not serve much of a purpose in modern codebases, so I've compiled this big ol' list of packages you may not need.
Caveat: The native APIs are not always direct replacements for existing packages, so as with any technical decision, the right choice depends on the context.
1. chalk (319.8M weekly downloads)
Node's util.styleText()
adds colors and styles to console output. It supports standard ANSI styles like bold, underline, and colors. The API is simple and dependency-free.
import { styleText } from 'node:util';
console.log(styleText('green', '✔ done'));
console.error(styleText('bold', styleText('red', 'error')));
To learn more, see util.styleText() (nodejs.org).
2. readable-stream (185.6M weekly downloads)
The core stream
module is stable and feature-complete. It supports async iterators and includes Promise-based helpers in stream/promises
. Most stream compatibility issues are resolved.
import { pipeline } from 'node:stream/promises';
import { createReadStream, createWriteStream } from 'node:fs';
await pipeline(
createReadStream('in.txt'),
createWriteStream('out.txt')
);
To learn more, see Node.js Streams (nodejs.org).
3. safe-buffer (164.1M weekly downloads)
Modern Node uses safe buffer constructors by default. Buffer.from()
and Buffer.alloc()
prevent common security issues. The unsafe new Buffer()
is deprecated.
const buf = Buffer.from('hello', 'utf8');
To learn more, see Buffer (nodejs.org).
4. isarray (132.0M weekly downloads)
Array.isArray()
is the standard way to check for arrays. It's been part of JavaScript since ES5.
if (Array.isArray(value)) {
// handle array
}
To learn more, see Array.isArray() (developer.mozilla.org).
5. fs-extra (113.3M weekly downloads)
Core fs
now includes the convenience methods that fs-extra
popularized. fs.cp
handles recursive copies, fs.rm
does robust removal, and fs.mkdir
creates parent directories. Promise variants live in node:fs/promises
.
import { cp, rm, mkdir } from 'node:fs/promises';
await mkdir('dist', { recursive: true });
await cp('assets', 'dist/assets', { recursive: true });
await rm('dist/tmp', { recursive: true, force: true });
To learn more, see File System (nodejs.org).
6. uuid (161.3M weekly downloads)
crypto.randomUUID()
generates RFC-4122 version 4 UUIDs. The implementation is cryptographically secure and requires no dependencies.
import { randomUUID } from 'node:crypto';
const id = randomUUID();
To learn more, see crypto.randomUUID() (nodejs.org).
7. glob (208.5M weekly downloads)
Node includes fs.glob()
for pattern matching in the filesystem. It returns an async iterator that yields matching paths. The API supports standard glob patterns.
import { glob } from 'node:fs/promises';
for await (const file of glob('src/**/*.ts')) {
console.log(file);
}
To learn more, see fsPromises.glob() (nodejs.org).
8. ws (117.3M weekly downloads)
WebSocket client support is built into Node as a global. The API is browser-compatible, so you can connect to WS and WSS endpoints without additional packages.
const ws = new WebSocket('wss://echo.websocket.events');
ws.onopen = () => ws.send('hello');
ws.onmessage = (ev) => console.log('echo:', ev.data);
To learn more, see WebSocket (nodejs.org).
9. qs (100.7M weekly downloads)
WHATWG URLSearchParams
constructs and serializes query strings safely. It pairs naturally with the URL
constructor and fetch()
.
const params = new URLSearchParams({ q: 'node', page: '1' });
const url = new URL('https://api.example.com/search');
url.search = params.toString();
const res = await fetch(url);
To learn more, see URLSearchParams (nodejs.org).
10. rimraf (97.9M weekly downloads)
Use fs.rm
with { recursive: true, force: true }
for cross-platform directory removal. It handles non-empty directories and missing paths gracefully.
import { rm } from 'node:fs/promises';
await rm('build', { recursive: true, force: true });
To learn more, see fs.rm() (nodejs.org).
11. mkdirp (103.2M weekly downloads)
The fs.mkdir
method accepts { recursive: true }
to create parent folders. This works in both callback and Promise forms.
import { mkdir } from 'node:fs/promises';
await mkdir('var/log/app', { recursive: true });
To learn more, see fs.mkdir() (nodejs.org).
12. pify (81.6M weekly downloads)
util.promisify()
converts callback-based functions to Promises. It handles Node's callback convention automatically.
import { promisify } from 'node:util';
import { stat } from 'node:fs';
const statAsync = promisify(stat);
console.log(await statAsync('package.json'));
To learn more, see util.promisify() (nodejs.org).
13. axios (65.8M weekly downloads)
Native fetch()
covers most axios use cases. It handles JSON requests and responses automatically. Timeouts work through AbortController with abort signals.
const ac = new AbortController();
const t = setTimeout(() => ac.abort(), 8_000);
try {
const res = await fetch('https://example.com/users', { signal: ac.signal });
const users = await res.json();
console.log(users);
} finally {
clearTimeout(t);
}
To learn more, see fetch() (nodejs.org).
14. async (67.0M weekly downloads)
JavaScript's async
/await
and Promise utilities eliminate most async flow-control needs. Methods like Promise.all()
and Promise.race()
handle concurrency patterns. The language has first-class support for asynchronous operations.
async function loadAll(urls) {
const resps = await Promise.all(urls.map((u) => fetch(u)));
return Promise.all(resps.map((r) => r.json()));
}
To learn more, see Async functions (developer.mozilla.org).
15. node-fetch (74.3M weekly downloads)
The Fetch API is now global in Node.js. You get the same Web-standard API that browsers use. JSON parsing, streaming bodies, and AbortController work out of the box.
const res = await fetch('https://api.example.com/data');
if (!res.ok) throw new Error(res.statusText);
const json = await res.json();
To learn more, see fetch() (nodejs.org).
16. object-assign (64.2M weekly downloads)
Object.assign()
and spread syntax handle shallow object merging. Both are part of the JavaScript standard.
const target = { a: 1 };
Object.assign(target, { b: 2 });
// or: const merged = { ...objA, ...objB };
To learn more, see Object.assign() (developer.mozilla.org).
17. performance-now (23.5M weekly downloads)
Node's perf_hooks.performance.now()
provides high‑resolution timing. The API matches the Web Performance API and reports time in milliseconds with sub‑millisecond precision.
import { performance } from 'node:perf_hooks';
const t0 = performance.now();
// work
console.log('elapsed ms:', performance.now() - t0);
To learn more, see performance.now() (nodejs.org).
18. array-flatten (37.0M weekly downloads)
Array.prototype.flat()
flattens nested arrays to any depth. The flatMap()
method combines mapping and flattening.
const nested = [1, [2, [3]]];
const flat = nested.flat(Infinity); // [1, 2, 3]
To learn more, see Array.flat() (developer.mozilla.org).
19. bluebird (31.9M weekly downloads)
Native Promises combined with util.promisify()
replace custom promise libraries. Node's filesystem and stream APIs already offer Promise-based versions.
import { promisify } from 'node:util';
import { readFile } from 'node:fs';
const readFileAsync = promisify(readFile);
const txt = await readFileAsync('README.md', 'utf8');
To learn more, see util.promisify() (nodejs.org).
20. url-parse (26.8M weekly downloads)
The WHATWG URL
class handles URL parsing and manipulation. It's spec-compliant and includes URLSearchParams
for query strings.
const url = new URL('https://user:pass@host:8080/path?a=1#frag');
url.searchParams.set('page', '2');
console.log(url.toString());
To learn more, see URL class (nodejs.org).
21. abort-controller (25.2M weekly downloads)
AbortController
and AbortSignal
are Node globals. They work across multiple APIs including fetch, fs, and timers. This provides a unified cancellation mechanism.
const ac = new AbortController();
setTimeout(() => ac.abort('too slow'), 2000);
try {
const res = await fetch('https://example.com/slow', { signal: ac.signal });
console.log(await res.text());
} catch (err) {
console.error('aborted:', err.name);
}
To learn more, see AbortController (nodejs.org).
22. request (13.4M weekly downloads)
The deprecated request
package is fully replaced by native fetch()
. The Fetch API provides a cleaner, Promise-based interface for HTTP requests.
const res = await fetch('https://example.com/login', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ email, password })
});
const data = await res.json();
To learn more, see fetch() (nodejs.org).
23. cross-fetch (20.0M weekly downloads)
Since fetch()
is global in Node, cross-environment polyfills are unnecessary. The same code works in browsers and Node.
const res = await fetch('https://example.com/ping');
console.log(await res.text());
To learn more, see fetch() (nodejs.org).
24. dotenv (63.3M weekly downloads)
Node now loads environment variables from .env
files without external packages. The --env-file
CLI flag and process.loadEnvFile()
method handle this natively. This eliminates a dependency that appears in almost every Node.js project.
// programmatic
import process from 'node:process';
process.loadEnvFile('.env');
console.log(process.env.API_KEY);
// CLI
// $ node --env-file=.env app.mjs
To learn more, see process.loadEnvFile() (nodejs.org).
25. form-data (100.9M weekly downloads)
The Web-standard FormData
API is global in Node. It integrates directly with fetch()
for multipart uploads. File attachments work through the Blob API.
const form = new FormData();
form.append('name', 'Ada');
form.append('file', new Blob(['hello']), 'greeting.txt');
await fetch('https://example.com/upload', { method: 'POST', body: form });
To learn more, see FormData (nodejs.org).
26. minimist (80.5M weekly downloads)
Command-line argument parsing is built into Node through util.parseArgs()
. It supports typed flags, short aliases, and positional arguments. The API returns a clean object structure for easy handling.
import { parseArgs } from 'node:util';
const { values, positionals } = parseArgs({
args: process.argv.slice(2),
options: {
port: { type: 'string', short: 'p' },
verbose: { type: 'boolean', short: 'v' }
}
});
console.log(values, positionals);
To learn more, see util.parseArgs() (nodejs.org).
27. nodemon (7.8M weekly downloads)
Node's --watch
flag restarts your process when files change. It integrates with the standard CLI and Node's test runner.
node --watch server.mjs
To learn more, see --watch flag (nodejs.org).
28. worker-farm (3.6M weekly downloads)
The worker_threads
module handles parallel CPU-bound tasks. It supports SharedArrayBuffer and structured cloning. Workers can share memory and transfer data efficiently.
import { Worker } from 'node:worker_threads';
const worker = new Worker(new URL('./worker.js', import.meta.url), { workerData: 42 });
worker.on('message', (msg) => console.log('result:', msg));
To learn more, see worker_threads (nodejs.org).
29. esm (3.5M weekly downloads)
ECMAScript modules are fully supported in Node. Set "type": "module"
in package.json
or use .mjs
files. CommonJS interop is supported.
// package.json: { "type": "module" }
import { readFile } from 'node:fs/promises';
console.log(await readFile('file.txt', 'utf8'));
To learn more, see ES Modules (nodejs.org).
30. left-pad (1.2M weekly downloads)
String padding is a language feature through padStart()
and padEnd()
. These methods handle padding with any character to any length.
const invoiceNo = String(42).padStart(6, '0'); // "000042"
To learn more, see String.padStart() (developer.mozilla.org).
31. sleep (134.3K weekly downloads)
The timers/promises
API provides Promise-based delays. It integrates with async/await and supports AbortSignal for cancellation.
import { setTimeout as delay } from 'node:timers/promises';
await delay(500); // sleep 500ms
To learn more, see timers/promises (nodejs.org).
Top comments (1)
This one is gold - I was still using setTimeouts 🤓