Unused production dependencies are dead weight with live consequences: they bloat the bundle, slow install times, expand the attack surface, and create maintenance debt for packages that will never receive targeted security updates. moment (60KB gzipped), full lodash (70KB gzipped), and date-fns without tree-shaking are common culprits found installed for a single date formatting call. Beyond bundle size, ISO 25010 performance-efficiency.resource-utilization counts every unnecessarily included module as a resource waste — and npm audit flags transitive vulnerabilities in packages you don't actually use.
Medium because unused large dependencies directly add payload to the bundle and expand the vulnerability surface area, even when the package itself works correctly.
Audit production dependencies against actual import usage. Remove packages that are never imported; replace heavy packages that are used for a single function with a lighter alternative or native implementation.
# Identify unused dependencies
npx depcheck
# Remove confirmed-unused packages
npm uninstall moment lodash
// Before — 60KB for one function
import moment from 'moment'
const formatted = moment(date).format('YYYY-MM-DD')
// After — native, zero bytes added
const formatted = new Date(date).toISOString().slice(0, 10)
// Or tree-shakeable alternative
import { format } from 'date-fns'
const formatted = format(date, 'yyyy-MM-dd')
// Before — 70KB lodash for one utility
import _ from 'lodash'
const result = _.debounce(fn, 300)
// After — tree-shakeable import
import debounce from 'lodash-es/debounce'
ID: performance-load.bundle.no-unused-dependencies
Severity: medium
What to look for: Count all production dependencies in package.json. For each dependency over 50KB (bundled size estimate), search source files for actual import statements. Classify each as: (a) fully used, (b) partially used (only 1-2 functions imported from a large library), or (c) unused (listed in package.json but never imported). Enumerate: "X production dependencies, Y actually imported, Z unused or partially used."
Pass criteria: No more than 1 production dependency is completely unused (listed but never imported). No more than 2 large libraries (over 50KB bundled) are partially used with less than 10% of their API surface consumed. Report: "X of Y production dependencies are actively imported in source code."
Do NOT pass when: A dependency like lodash or moment is imported in only 1 file using 1 function — this is a large unused dependency even if technically "imported."
Fail criteria: 2 or more large production dependencies (lodash, moment, date-fns without tree-shaking) are installed but unused, or only a small fraction of their functionality is used.
Skip (N/A) when: Never — unused dependencies bloat the bundle.
Detail on fail: "3 of 18 production dependencies are unused — lodash (70KB), moment (60KB), and classnames (5KB) are listed but never imported" or "moment imported in 1 file for 1 function — 60KB added to bundle for Date.now() equivalent"
Remediation: Remove unused dependencies and use tree-shakeable alternatives:
# Find unused dependencies
npm prune
npm ls — unused (npm@v7+)
# Remove unused packages
npm uninstall lodash moment
// Before — heavy dependencies
import _ from 'lodash'
import moment from 'moment'
// After — lighter alternatives
import { debounce } from 'lodash-es' // tree-shakeable
import { formatDate } from 'date-fns' // small by default
// Or use native utilities
const debounce = (fn, delay) => { /* ... */ }