depcheck, npm-check-updates, retire, and snyk are CLI tools that help developers maintain healthy, secure, and up-to-date npm dependency trees. While they share the broad goal of improving project hygiene, each targets a distinct aspect: unused dependencies (depcheck), outdated packages (npm-check-updates), known vulnerable libraries in frontend assets (retire), and comprehensive vulnerability scanning with remediation guidance (snyk). Together, they form complementary layers of a modern JavaScript dependency management strategy.
Managing npm dependencies isn’t just about installing packages — it’s about keeping your project lean, current, and secure. The tools depcheck, npm-check-updates, retire, and snyk each tackle a different slice of this problem. Let’s compare them head-to-head through real engineering scenarios.
depcheck analyzes your codebase to find packages listed in package.json that aren’t actually imported or required anywhere.
# Run depcheck to list unused dependencies
npx depcheck
It parses JavaScript, TypeScript, and common config files to trace static imports. However, it won’t detect:
require() (e.g., require(someVar))npx or scripts in package.jsonExample output might show:
Unused dependencies
* lodash
* moment
In contrast, npm-check-updates, retire, and snyk do not check for unused code — they assume everything in package.json is needed.
npm-check-updates (often aliased as ncu) tells you which packages have newer versions available and can update package.json for you.
# Show outdated packages
npx npm-check-updates
# Update package.json to latest majors
npx npm-check-updates -u
npm install
It respects your package.json version ranges and can target specific update strategies (e.g., only minors, or latest tags). This is purely a versioning tool — it doesn’t assess security or usage.
The other tools handle updates differently:
depcheck: No version checking at all.retire: Reports vulnerable versions but doesn’t suggest non-vulnerable alternatives or update package.json.snyk: Recommends specific safe versions to upgrade to (or patches) based on vulnerability data.# Snyk shows fixable issues with upgrade paths
npx snyk test
npx snyk fix # applies safe upgrades automatically
This is where the tools diverge sharply in scope.
retire focuses only on client-side JavaScript libraries. It scans your project’s public assets (like dist/ or public/) and node_modules for known vulnerable versions of frontend frameworks.
# Scan for vulnerable JS libraries in ./public
npx retire --js --outputformat json
It uses a local database of CVE-like entries for libraries such as React, Vue, or Handlebars. But it won’t catch vulnerabilities in server-side packages like express or lodash when used in Node.js.
snyk, by comparison, scans your entire dependency tree — including deeply nested transitive dependencies — for vulnerabilities in both frontend and backend contexts. It pulls from a continuously updated vulnerability database and understands exploitability in your specific environment.
# Snyk scans node_modules and lockfile for all vulns
npx snyk test
Output includes CVSS scores, exploit maturity, and whether the vulnerable function is actually reachable in your code.
depcheck and npm-check-updates do not perform any vulnerability detection.
Finding issues is only half the battle. How easy is it to act?
depcheck: You manually remove unused entries from package.json and run npm install.npm-check-updates: Use -u flag to auto-update package.json, then run install.retire: Only reports — you must manually choose a safe version and update yourself.snyk: Offers snyk fix to automatically apply safe upgrades or patches without breaking changes.# Snyk can patch without version bumps
npx snyk fix
# Applies a targeted patch to vulnerable code
This makes snyk uniquely valuable in regulated or high-compliance environments where audit trails and minimal-change fixes matter.
| Tool | Scans package.json | Analyzes Source Code | Checks node_modules | Scans Public Assets | Understands Transitive Deps |
|---|---|---|---|---|---|
depcheck | ✅ | ✅ (static imports) | ❌ | ❌ | ❌ |
npm-check-updates | ✅ | ❌ | ❌ | ❌ | ❌ |
retire | ❌ | ❌ | ✅ (frontend libs only) | ✅ | ⚠️ (limited) |
snyk | ✅ | ❌ | ✅ | ⚠️ (via deps) | ✅ |
💡 Note:
snykuses yourpackage-lock.jsonoryarn.lockto reconstruct the full dependency graph, including transitive dependencies that may not appear inpackage.json.
depcheck to remove unused deps → reduces bundle size and attack surface.npm-check-updates -u to bump to latest non-breaking versions.retire to catch vulnerable client-side libraries missed by other tools.snyk test to find and snyk fix to patch remaining vulnerabilities.This layered approach catches issues no single tool can address alone.
depcheck and npm-check-updates — they don’t understand exploit context.snyk test immediately: it will tell you if you’re affected and how to fix it.snyk monitor to get alerts on future vulnerabilities.retire might help if the zero-day affects a frontend library, but it lacks remediation guidance.
depcheck may falsely flag packages used in non-standard ways (e.g., ESLint plugins, Webpack loaders). Use --ignores to suppress false positives.npm-check-updates doesn’t guarantee compatibility — always test after major upgrades.retire’s database is less comprehensive than Snyk’s and hasn’t seen frequent updates in recent years. Verify its findings against official advisories.snyk requires authentication for full features (snyk auth), and the free tier has monthly scan limits. Offline mode is limited.| Goal | Best Tool(s) |
|---|---|
| Remove unused dependencies | depcheck |
| Update to latest package versions | npm-check-updates |
| Find vulnerable frontend JS libraries | retire |
| Comprehensive security scanning & fixing | snyk |
| Routine dependency maintenance | npm-check-updates + snyk |
| Reducing bundle size | depcheck |
Think of these tools as layers in a defense-in-depth strategy:
depcheck to keep your dependency list honest.npm-check-updates regularly to stay current.retire if you ship a lot of third-party frontend code.snyk for security-critical projects.No single tool replaces the others — together, they give you visibility into usage, freshness, and risk across your entire JavaScript stack.
Choose depcheck when you need to identify and remove unused or extraneous npm dependencies from your project. It’s especially useful during refactoring, tech debt cleanup, or before major version upgrades to slim down package.json. However, be aware it may miss dynamically required modules (e.g., via require(variable)) or dependencies used only in build scripts or non-JavaScript files like CSS imports.
Choose npm-check-updates when your primary goal is to update dependencies to their latest compatible or major versions. It excels at showing what’s outdated and can automatically update package.json, making it ideal for routine maintenance. Use it before security scans to ensure you’re not fixing vulnerabilities in versions that could simply be upgraded.
Choose snyk when you require deep, actionable security intelligence across both frontend and backend JavaScript dependencies, including transitive ones. It identifies vulnerabilities with severity context, suggests precise upgrade paths or patches, and integrates into CI/CD pipelines. Note that full remediation features require authentication and may involve usage limits under the free tier.
Choose retire if you specifically need to scan frontend JavaScript files (including those in node_modules) for known vulnerabilities in client-side libraries like jQuery, Bootstrap, or Angular. It’s lightweight and focused on browser-facing code but does not analyze server-side Node.js dependencies or provide fix recommendations beyond version updates.
Depcheck is a tool for analyzing the dependencies in a project to see: how each dependency is used, which dependencies are useless, and which dependencies are missing from package.json.
npm install -g depcheck
Or simply using npx which is a package runner bundled in npm:
$ npx depcheck
Notice: depcheck needs node.js >= 10.
Depcheck not only recognizes the dependencies in JavaScript files, but also supports these syntaxes:
typescript dependency)@vue/compiler-sfc dependency)To get the syntax support by external dependency, please install the corresponding package explicitly. For example, for TypeScript user, install depcheck with typescript package:
npm install -g depcheck typescript
The special component is used to recognize the dependencies that are not generally used in the above syntax files. The following scenarios are supported by specials:
babel - Babel presets and pluginsbin - Dependencies used in npm commands, Travis scripts or other CI scriptscommitizen - Commitizen configuration adaptoreslint - ESLint configuration presets, parsers and pluginsfeross-standard - Feross standard format parsergatsby - Gatsby configuration parsergulp-load-plugins - Gulp-load-plugins lazy loaded pluginshusky - Husky configuration parseristanbul - Istanbul nyc configuration extensionsjest - Jest properties in Jest Configurationkarma - Karma configuration frameworks, browsers, preprocessors and reporterslint-staged - Lint-staged configuration parsermocha - Mocha explicit required dependenciesprettier - Prettier configuration moduletslint - TSLint configuration presets, parsers and pluginsttypescript - ttypescript transformerswebpack - Webpack loadersserverless- Serverless pluginsThe logic of a special is not perfect. There might be false alerts. If this happens, please open an issue for us.
depcheck [directory] [arguments]
The directory argument is the root directory of your project (where the package.json file is). If unspecified, defaults to current directory.
All of the arguments are optional:
--ignore-bin-package=[true|false]: A flag to indicate if depcheck ignores the packages containing bin entry. The default value is false.
--skip-missing=[true|false]: A flag to indicate if depcheck skips calculation of missing dependencies. The default value is false.
--json: Output results in JSON. When not specified, depcheck outputs in human friendly format.
--oneline: Output results as space separated string. Useful for copy/paste.
--ignores: A comma separated array containing package names to ignore. It can be glob expressions. Example, --ignores="eslint,babel-*".
--ignore-dirs: DEPRECATED, use ignore-patterns instead. A comma separated array containing directory names to ignore. Example, --ignore-dirs=dist,coverage.
--ignore-path: Path to a file with patterns describing files to ignore. Files must match the .gitignore spec. Example, --ignore-path=.eslintignore.
--ignore-patterns: Comma separated patterns describing files to ignore. Patterns must match the .gitignore spec. Example, --ignore-patterns=build/Release,dist,coverage,*.log.
--quiet: Suppress the "No depcheck issue" log. Useful in a monorepo with multiple packages to focus only on packages with issues.
--help: Show the help message.
--parsers, --detectors and --specials: These arguments are for advanced usage. They provide an easy way to customize the file parser and dependency detection. Check the pluggable design document for more information.
--config=[filename]: An external configuration file (see below).
Depcheck can be used with an rc configuration file. In order to do so, create a .depcheckrc file in your project's package.json folder, and set the CLI keys in YAML, JSON, and JavaScript formats.
For example, the CLI arguments --ignores="eslint,babel-*" --skip-missing=true would turn into:
.depcheckrc
ignores: ["eslint", "babel-*"]
skip-missing: true
Important: if provided CLI arguments conflict with configuration file ones, the CLI ones will take precedence over the rc file ones.
The rc configuration file can also contain the following extensions: .json, .yaml, .yml.
Similar options are provided to depcheck function for programming:
import depcheck from 'depcheck';
const options = {
ignoreBinPackage: false, // ignore the packages with bin entry
skipMissing: false, // skip calculation of missing dependencies
ignorePatterns: [
// files matching these patterns will be ignored
'sandbox',
'dist',
'bower_components',
],
ignoreMatches: [
// ignore dependencies that matches these globs
'grunt-*',
],
parsers: {
// the target parsers
'**/*.js': depcheck.parser.es6,
'**/*.jsx': depcheck.parser.jsx,
},
detectors: [
// the target detectors
depcheck.detector.requireCallExpression,
depcheck.detector.importDeclaration,
],
specials: [
// the target special parsers
depcheck.special.eslint,
depcheck.special.webpack,
],
package: {
// may specify dependencies instead of parsing package.json
dependencies: {
lodash: '^4.17.15',
},
devDependencies: {
eslint: '^6.6.0',
},
peerDependencies: {},
optionalDependencies: {},
},
};
depcheck('/path/to/your/project', options).then((unused) => {
console.log(unused.dependencies); // an array containing the unused dependencies
console.log(unused.devDependencies); // an array containing the unused devDependencies
console.log(unused.missing); // a lookup containing the dependencies missing in `package.json` and where they are used
console.log(unused.using); // a lookup indicating each dependency is used by which files
console.log(unused.invalidFiles); // files that cannot access or parse
console.log(unused.invalidDirs); // directories that cannot access
});
The following example checks the dependencies under /path/to/my/project folder:
$> depcheck /path/to/my/project
Unused dependencies
* underscore
Unused devDependencies
* jasmine
Missing dependencies
* lodash
It figures out:
underscore is declared in the package.json file, but not used by any code.jasmine is declared in the package.json file, but not used by any code.lodash is used somewhere in the code, but not declared in the package.json file.Please note that, if a subfolder has a package.json file, it is considered another project and should be checked with another depcheck command.
The following example checks the same project, however, outputs as a JSON blob. Depcheck's JSON output is in one single line for easy pipe and computation. The json command after the pipe is a node.js program to beautify the output.
$> depcheck /path/to/my/project --json | json
{
"dependencies": [
"underscore"
],
"devDependencies": [
"jasmine"
],
"missing": {
"lodash": [
"/path/to/my/project/file.using.lodash.js"
]
},
"using": {
"react": [
"/path/to/my/project/file.using.react.jsx",
"/path/to/my/project/another.file.using.react.jsx"
],
"lodash": [
"/path/to/my/project/file.using.lodash.js"
]
},
"invalidFiles": {
"/path/to/my/project/file.having.syntax.error.js": "SyntaxError: <call stack here>"
},
"invalidDirs": {
"/path/to/my/project/folder/without/permission": "Error: EACCES, <call stack here>"
}
}
dependencies, devDependencies and missing properties have the same meanings in the previous example.using property is a lookup indicating each dependency is used by which files.missing and using lookup is an array. It means the dependency may be used by many files.invalidFiles property contains the files having syntax error or permission error. The value is the error details. However, only one error is stored in the lookup.invalidDirs property contains the directories having permission error. The value is the error details.Depcheck just walks through all files and tries to find the dependencies according to some predefined rules. However, the predefined rules may not be enough or may even be wrong.
There may be some cases in which a dependency is being used but is reported as unused, or a dependency is not used but is reported as missing. These are false alert situations.
If you find that depcheck is reporting a false alert, please open an issue with the following information to let us know:
depcheck --json command. Beautified JSON is better.We use the GitHub release page to manage changelog.
This project exists thanks to all the people who contribute. [Contribute].
Become a financial contributor and help us sustain our community. [Contribute]
Support this project with your organization. Your logo will show up here with a link to your website. [Contribute]
MIT License.