Skip to main content

Most Depended-On npm Packages 2026

·PkgPulse Team
0

TL;DR

A handful of packages support the entire npm ecosystem. Semver, minimatch, glob, lodash, chalk — these have millions of dependent packages and are effectively critical infrastructure. The famous leftpad incident of 2016 (75 lines, 11 dependents) broke the internet. Today's risk is concentrated in hundreds of utility packages that everything else depends on. Knowing the dependency tree of your dependencies is supply chain security.

Key Takeaways

  • semver has 95M dependents — every package manager depends on it
  • lodash has 160K direct dependents — slowly declining as native JS covers more ground
  • glob/minimatch: 60M+ dependents — used by every build tool on the planet
  • The real risk: single-maintainer packages deep in your dependency tree
  • Modern alternatives exist for most foundational packages — often built-in to Node.js

The Dependency Graph You Don't See

When you run npm install on a project with 50 dependencies listed in package.json, you're not installing 50 packages — you might be installing 500, or 1,500, or in large enterprise projects, 3,000+. These are transitive dependencies: the dependencies of your dependencies, and the dependencies of those. Most developers know this intellectually but rarely reckon with the full shape of it.

The leftpad incident of March 2016 is the canonical example, but it's worth examining precisely. leftpad was a 75-line function that left-padded strings. It had 11 direct dependents — meaning 11 packages listed it explicitly in their package.json. But those 11 packages were themselves widely depended-upon, so when the author unpublished leftpad in a dispute with Kik over package naming, builds broke across hundreds of thousands of projects that had never heard of leftpad and had no reason to know it was in their dependency tree. Node.js, Babel, React, and dozens of major tools all failed to build simultaneously.

What "most depended-on" actually measures is influence in the transitive dependency graph, not just direct adoption. A package with 95 million dependents, like semver, is in the dependency tree of essentially every non-trivial Node.js project that has ever been built. Not because developers chose it — because the tools they chose chose it. npm itself depends on semver. Yarn depends on semver. Webpack depends on semver. When you install any of these, semver comes along.

This matters for security analysis in a specific way. A vulnerability in a package with 95 million dependents doesn't mean 95 million applications need to patch immediately — many of those dependents are themselves transitive dependencies of inactive projects that haven't been built in years. But it does mean the blast radius of a malicious publish or a critical CVE is enormous, because the dependency graph is the propagation mechanism. The hidden cost of npm dependencies is partly about this: you're not just accepting the risk profile of the packages you chose, you're accepting the aggregate risk profile of everything they chose.

Understanding this graph is increasingly non-optional. The npm registry now shows dependent counts on package pages. Tools like npm ls --all will print your full tree. The question isn't whether you have deeply nested dependencies — you do, every project does — it's whether you understand where your critical-path packages sit in that graph and who maintains them.


Tier 1: The Foundation (Millions of Dependents)

semver — ~95M dependents

npm install semver
# Used by: npm, yarn, pnpm, every package manager, every tool that reads package.json
# What it does: parses and compares semantic version strings
# Maintainer: npm (npm, Inc. / GitHub)
# Security track record: CVE-2022-25883 (ReDoS) — patched quickly
# Risk level: LOW (well-funded, institutional maintenance)

import semver from 'semver';
semver.satisfies('1.2.3', '^1.0.0')  // true
semver.gt('2.0.0', '1.9.9')          // true

glob / minimatch — ~60M dependents combined

# glob: file system glob pattern matching
# minimatch: the pattern matching engine underneath
# Used by: webpack, jest, eslint, prettier, TypeScript, every build tool

# Modern alternative: fast-glob (actively developed, faster)
npm install fast-glob  # 3x faster than glob, same patterns

# Or: Node.js 22+ built-in glob
import { glob } from 'node:fs/promises';
const files = await glob('src/**/*.ts');  # Native, no npm needed

chalk — ~50M dependents

# Terminal string styling: colors, bold, underline
# Used by: every CLI tool, jest output, webpack, prettier, eslint

import chalk from 'chalk';
console.log(chalk.blue('Hello'));
chalk.bold.red('Error message');

# Alternative for zero-dep: picocolors (3x smaller, faster)
import pc from 'picocolors';
console.log(pc.blue('Hello'));

# Note: chalk v5 is ESM-only; chalk v4 for CommonJS projects

Understanding Tier 1 Risk

The counterintuitive finding about Tier 1 packages: they are often the safest packages in your dependency tree, despite having the largest blast radius if something goes wrong. semver is maintained by npm, Inc., which has direct financial and reputational incentive to keep it secure and patched. glob and minimatch are maintained by Isaac Schlueter, the creator of npm. Chalk has a dedicated team and corporate backing.

The 95 million dependents of semver means that a vulnerability in semver is a five-alarm incident at npm's security team — it would be picked up by automated scanning, disclosed, and patched on a timeline measured in days, not months. CVE-2022-25883 (the ReDoS vulnerability) was patched in 24 hours and the fix propagated quickly because the distribution channel (npm itself) is the same tool that depends on the package.

For teams managing supply chain risk, the practical implication is to spend less time worrying about Tier 1 packages and more time on what's in Tier 2 and especially the long tail of single-maintainer utilities in Tier 3. The dependency tree analysis covers the nesting depth problem specifically — some package trees go 15+ levels deep, and it's the middle levels where unmaintained packages tend to lurk.


Tier 2: The Utility Layer (100K+ Dependents)

lodash — ~160K direct dependents

# JavaScript utility library: arrays, objects, functions, strings
# At peak: 35M weekly downloads
# Now: ~28M weekly downloads (declining slowly as native JS improves)
# Dependents: ~160K packages depend on it

# Why it persists:
# 1. Extremely battle-tested (20+ years)
# 2. Zero dependencies
# 3. Handles edge cases that native methods don't

# What native JS now covers:
# _.cloneDeep → structuredClone() (Node 17+)
# _.flatten → Array.flat()
# _.get → optional chaining ?.
# _.defaultTo → nullish coalescing ??
# _.uniq → [...new Set(arr)]

# Still better in lodash:
# _.merge (deep merge) → no native equivalent
# _.throttle, _.debounce → use lodash or a tiny package
# _.isEqual (deep equality) → no clean native alternative

is-xxx packages (100+ packages, millions of dependents)

# is-string, is-number, is-array, is-object, is-plain-object...
# These tiny packages exist because JavaScript typeof is notoriously inconsistent

# typeof null === 'object'  ← famous JS quirk
# Array.isArray() exists but Object.isObject() doesn't

# Modern replacement: TypeScript + Zod handle this at type level
# Runtime: use built-ins where possible

# is-number → typeof n === 'number' && !isNaN(n)
# is-array → Array.isArray(arr)
# is-string → typeof s === 'string'

debug — ~65K dependents

# Tiny debug logging utility used by countless packages
npm install debug
# DEBUG=express:* node app.js  ← enables debug output

# You've likely seen: "[express] router: dispatching GET /" in your console
# That's debug in action — used by express, mongoose, socket.io, many more

# Modern replacement: pino child loggers (for your own code)
# But debug in dependencies: just accept it, it's stable

Understanding Tier 2 Risk

Tier 2 is where the trade-off analysis gets more nuanced. Lodash at 160,000 direct dependents is battle-tested but slowly declining — not because it has problems, but because native JavaScript is finally catching up to the conveniences it pioneered. The 28 million weekly downloads look large, but the composition has changed: fewer new projects choosing lodash, more legacy projects that can't or won't remove it.

The practical guidance for lodash is case-by-case. For new code in your application layer, prefer native alternatives where they exist — structuredClone over _.cloneDeep, optional chaining over _.get. For existing code that uses lodash heavily, the migration cost often exceeds the benefit unless you're facing specific bundle size constraints. See the lodash vs underscore comparison for a detailed look at the maintenance trajectories of both packages.

The is-xxx micro-package family is a different risk profile: each individual package is trivially small, but they're maintained by a small number of individual developers with no institutional backing. The risk isn't a security vulnerability — these packages are too simple for that. The risk is abandonment and the slow decay that follows: unpatched CVEs in their own (rare) dependencies, Node.js version compatibility issues, ESM migration incompleteness. For your own code, replacing them with inline type checks is straightforward. For packages in your dependency tree that pull them in transitively, you can't control it — but you can monitor for unpatched known vulnerabilities.


Tier 3: Security-Critical Utilities

node-forge / node-crypto wrappers — varies

# Many packages wrap Node.js crypto for common operations
# These are high-risk: cryptography bugs can be silent and catastrophic

# Key packages in dependency trees:
# - bcryptjs: password hashing
# - jsonwebtoken (jwt): auth tokens
# - node-forge: TLS/cryptography primitives

# Best practice: prefer packages with:
# ✅ Active CVE scanning (Snyk or GitHub security advisories)
# ✅ Recent security review by a third party
# ✅ High download counts (more eyes = faster bug detection)
# ✅ Zero dependencies or well-audited deps only

parse libraries (csv-parse, xml2js, etc.)

# Input parsing libraries are common attack vectors:
# - Buffer overflows from malformed input
# - ReDoS from regex on user input
# - Prototype pollution (fixed in most, but check older versions)

# Example: prototype pollution was found in:
# - lodash <4.17.21
# - minimist <1.2.6
# - ajv (older versions)
# - Set of deep merge utilities

# Check your dep tree:
npm audit  # Will catch known CVEs
# Or: use Socket.dev for behavioral analysis

Understanding Tier 3 Risk

Tier 3 security-critical utilities are where supply chain incidents tend to start. Cryptography wrappers like jsonwebtoken are high-value targets: a vulnerability in token validation can mean authentication bypass, which is game over for the application. The jsonwebtoken package itself had a critical vulnerability (CVE-2022-23529) that allowed arbitrary code execution in certain configurations. It was discovered, disclosed, and patched — but teams running old pinned versions don't get automatic protection.

The parse library category is interesting because it sits at the boundary of untrusted input. A CSV parser processing user-uploaded files is handling adversarial input by definition. ReDoS (Regular Expression Denial of Service) vulnerabilities in parse libraries are surprisingly common and surprisingly impactful: a crafted input string can cause exponential backtracking in a regex, hanging the process. minimist below version 1.2.6 had both a prototype pollution vulnerability and a ReDoS vector — and given that it's one of the most-depended-upon packages in the ecosystem, the exposure was enormous before patching.

For security-critical packages in your direct dependencies, the standard practice is: pin to a specific version, monitor the GitHub security advisories feed for that package, and set a calendar reminder to review the dependency every six months. For transitive dependencies, automated scanning via npm audit or Socket.dev is your main line of defense.


The Real Supply Chain Risk: Single-Maintainer Utilities

# The highest risk isn't lodash (institutional backing)
# It's the single-maintainer utility package buried in your dep tree

# How to find high-risk packages in YOUR project:
npx snyk test --all-projects
# Shows: dependency tree + vulnerability data

# Or: list all packages with single maintainer
npm ls --all 2>/dev/null | while read pkg; do
  echo "Checking $pkg..."
  npm view "$pkg" maintainers --json
done
# (simplified — use snyk or Socket for this in practice)

# What "high risk" looks like:
# - 1 maintainer, last active 18+ months ago
# - 50K+ packages depending on it
# - No corporate backing
# - npm 2FA not enabled (npm access 2fa-not-required)

# Real example from 2025: a single-maintainer utility
# package deep in 30K+ packages' dep trees was abandoned
# for 18 months with an unpatched ReDoS vulnerability

Real Incidents: When Core Dependencies Fail

Three incidents define the modern understanding of npm supply chain risk. Each taught the ecosystem something different, and the lessons compound.

The left-pad incident (March 2016) was the first time most developers realized that the npm ecosystem's decentralization was a structural vulnerability. Azer Koçulu, the package's author, unpublished 250+ of his packages in a dispute with Kik Messenger over a package naming conflict with their product. Among them was left-pad, a 75-line module that padded strings. Within minutes, CI pipelines across the world started failing. React's build broke. Babel's build broke. Node.js itself couldn't build from source. The incident prompted npm to implement policies against unpublishing packages with many dependents and introduced npm install --prefer-offline as a mitigation pattern. But the deeper lesson — that concentrating so much ecosystem infrastructure in micro-packages maintained by single developers creates systemic brittleness — wasn't fully addressed by policy changes.

The event-stream incident (November 2018) was more alarming because it was intentional. The original maintainer of event-stream, a package with millions of weekly downloads, transferred ownership to an unknown developer who had submitted a helpful pull request. The new maintainer published a malicious version that contained a backdoor specifically targeting the Copay Bitcoin wallet application — the injected code would steal private keys from users of that specific application. The malicious code was obfuscated and hidden inside a nested dependency (flatmap-stream) so it wouldn't be obvious to casual inspection. The incident exposed two failure modes that policies couldn't easily fix: the ease of ownership transfer on npm, and the fact that malicious code in transitive dependencies can be invisible to the projects that ship it.

The node-ipc incident (March 2022) was geopolitical. Brandon Nozaki Miller, the maintainer of node-ipc (a package with 3 million weekly downloads used by Vue CLI and other widely-used tools), published versions that contained a protest payload: code that would overwrite files with hearts and peace symbols on systems with Russian or Belarusian IP addresses. The payload ran on developer machines during npm install, not just in production. This incident highlighted that supply chain attacks don't require criminal intent — maintainer political action is a risk vector that traditional security scanning doesn't detect. The immediate response from affected projects was to pin to the last known-clean version. The longer-term response drove adoption of lockfile integrity checking and reproducible builds.

What we learned: left-pad showed that availability is a dependency; event-stream showed that ownership transfer is a security event; node-ipc showed that maintainer intent is a trust assumption you're making without knowing it. The 2026 supply chain security toolkit described below is a direct response to all three.


The 2026 Supply Chain Security Toolkit

The combination of high-profile incidents and increasing regulatory pressure (particularly NIST guidelines and CISA recommendations for software supply chain security) has driven adoption of a more systematic approach. These are the tools enterprise teams are using.

Socket.dev has become the leading behavioral analysis tool for npm packages. Unlike npm audit, which checks packages against a database of known CVEs, Socket analyzes the actual code of packages being added or updated. It flags: new network access in a package that didn't previously make network calls, filesystem access to sensitive paths, obfuscated code, eval usage, and install scripts that run code during npm install. These behavioral signals catch malicious packages that haven't been formally reported as CVEs yet. Socket's GitHub integration blocks pull requests that introduce packages with high-risk signals, with a one-click review UI for false positives.

Snyk covers both vulnerability scanning and license compliance — a requirement for enterprise legal reviews that npm audit doesn't address. Snyk's dependency graph visualization is also genuinely useful for understanding your transitive exposure: it shows you which direct dependency pulled in which vulnerable transitive dependency, with upgrade path recommendations. The free tier covers most individual developer needs; the paid tier adds team management and policy enforcement.

npm audit remains the baseline. It's free, built in, and catches known CVEs. Run it as part of every CI build with --audit-level=moderate to fail on anything above informational severity. The limitation is that it only knows about vulnerabilities that have been formally disclosed and added to the npm security database — the event-stream malicious code wouldn't have been caught by npm audit for the weeks between publish and detection.

SBOM (Software Bill of Materials) generation is increasingly required for enterprise and government customers. An SBOM is a machine-readable inventory of every package and version in your dependency tree, in a standardized format (SPDX or CycloneDX are the two main standards). Generating an SBOM is straightforward: npm sbom --sbom-format cyclonedx outputs a CycloneDX-format SBOM for your project. The harder part is the downstream use: SBOMs need to be stored, versioned with your releases, and fed into vulnerability scanning systems that can update their analysis as new CVEs are disclosed against packages you've already shipped.

What enterprise teams are doing differently in 2026 is treating supply chain security as a continuous process rather than a one-time audit. Automated scanning runs on every dependency update PR. SBOM generation is part of the release pipeline. Package updates are batched and reviewed weekly rather than merged automatically via Dependabot without review. The supply chain security guide covers the full workflow in detail.


Node.js Built-ins That Replaced npm Packages

The gradual maturation of the Node.js standard library has made a significant number of once-essential npm packages unnecessary. This matters for supply chain security because every package you remove is a dependency you no longer need to monitor, update, or trust.

node:fetch (Node 18+) replaced node-fetch and axios for basic HTTP in server contexts. The native implementation follows the Web Fetch API standard, the same as browsers, which also means code using it is more portable across runtimes.

crypto.randomUUID() (Node 15.6+) replaced uuid. The native implementation is backed by the OS's cryptographic random number generator and doesn't add a dependency. uuid is still widely used for its additional utility functions (v1, v3, v5 UUIDs) but for the common case of generating a v4 UUID, the built-in is strictly better.

structuredClone() (Node 17+) replaced lodash.clonedeep and similar deep clone utilities. It handles circular references correctly, clones more types than JSON serialization, and is built into the runtime. The performance is roughly equivalent to hand-rolled deep clone implementations.

fs.rm() with { recursive: true, force: true } (Node 14.14+) replaced rimraf. rimraf was one of the most-installed dev dependencies in npm history, used in build scripts to delete directories before rebuilds. The native option is equivalent and removes a dependency from every project that uses it.

fs.mkdir() with { recursive: true } (Node 10.12+) replaced mkdirp. Same pattern as rimraf — the native filesystem API caught up to what the package provided.

node:readline with createInterface (all Node versions) provides basic CLI prompt functionality without needing inquirer or prompts for simple cases. For complex interactive CLIs with complex menus and validation, inquirer remains the better option.

node:timers/promises (Node 15+) provides setTimeout and setInterval as promise-based APIs, replacing various utility packages that provided this wrapping.

node:test (Node 18+) is a built-in test runner that covers unit testing without requiring Jest or Vitest for simple test suites. It has a Mocha-compatible API and supports coverage via --experimental-test-coverage. For projects with complex test setups, Vitest is still the better choice — but for packages and tools with lightweight test requirements, node:test means zero testing dependencies.

DOMParser / URL / URLSearchParams (Node 18+ with globalThis) replaced packages like whatwg-url and various URL manipulation utilities. These Web API implementations in Node.js mean server code can share URL handling logic with browser code without an npm package.

The practical takeaway: before adding an npm package for a utility function, check whether Node.js 18+ or Node.js 20+ provides it natively. The Node.js changelog for each major version documents newly promoted APIs. Removing even a handful of utility dependencies from your package.json reduces your attack surface and simplifies your dependency graph.


Alternatives to Foundational Packages

PackageDependentsBetter AlternativeReason
glob30M+fast-glob3x faster, actively maintained
chalk50M+picocolors5x smaller, no deps
uuid40K+crypto.randomUUID()Built-in Node 18+
minimist40K+yargs-parserSafer, actively maintained
request75K+undici, ky, fetchrequest is deprecated
node-fetch25K+native fetchBuilt-in Node 18+
lodash.clonedeep60K+structuredClone()Built-in Node 17+
moment40K+dayjs, date-fns72KB → 2.7KB
rimraf50K+fs.rm()Built-in Node 14.14+
mkdirp30K+fs.mkdir recursiveBuilt-in Node 10.12+

Dependency Minimization Strategy

# Goal: reduce your attack surface

# Step 1: Find unused dependencies
npx depcheck
# Reports: Unused dependencies you can remove

# Step 2: Check bundle size of each dep
npx cost-of-modules --no-install

# Step 3: For each dep, ask:
# - Is there a Node.js built-in that does this now?
# - Is there a smaller alternative?
# - Is this dep maintained?
# - How many transitive deps does it pull in?

npm view package-name --json | jq '.dependencies | keys | length'
# 0 deps = much safer (no transitive risk)

# Step 4: For production servers, audit regularly:
npm audit --audit-level=moderate
# Review and address before they become critical

See dependency counts, health scores, and alternatives for any npm package at PkgPulse.

Compare Lodash and Underscore package health on PkgPulse.

See also: npm Dependency Trees: Most Nested Packages 2026, The Hidden Cost of npm Dependencies, and How to Secure Your npm Supply Chain in 2026.

The 2026 JavaScript Stack Cheatsheet

One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly.