| β | β οΈ (Misses TS semantics/transitive) | β | ~20% | Medium |
| Knip | β
(Full module graph) | β
(Exact usage tracking) | β
(Direct + implicit transitive) | β
(--fix) | <5% | Low (zero-config) |
Key Findings:
- Single-pass detection covers unused npm dependencies, unlisted dependencies, unused exports, unused files, and unresolved imports.
- Framework-agnostic plugin architecture (~150 plugins) auto-detects Vite, Vitest, Next.js, Astro, ESLint, and GitHub Actions without manual mapping.
- Production validation: Vercel reported removing ~300,000 lines of dead code using Knip, confirming scalability in enterprise-grade repositories.
Sweet Spot: TypeScript/JavaScript projects with complex module graphs, framework-specific entry points, or monorepo structures where cross-file dependency tracking exceeds manual audit feasibility.
Core Solution
Knip operates by tracing a complete dependency graph starting from explicitly defined entry points. Any module, export, or dependency not connected to the graph is flagged as dead code. The tool requires zero configuration in most environments due to automatic toolchain detection.
Installation and Usage
No installation required β just run it:
npx knip
Or add it to devDependencies:
npm install -D knip
npx knip
Reading the Output
After a run, the output looks something like this:
Unused files (2)
src/legacy/old-helper.ts
src/utils/deprecated.ts
Unused dependencies (3)
lodash
moment
@types/node (devDependencies)
Unused exports (5)
src/shared/helpers.ts: formatDate, parseQuery
src/utils/string.ts: capitalize, truncate, slugify
Each category isolates unreferenced files, removable packages, and dead exports. The first run typically surfaces high volume; prioritize unused dependencies first, then exports, then entire files.
Configuration
If customization is required, add a knip.json at the project root (or a knip key in package.json):
{
"entry": ["src/index.ts", "src/pages/**/*.tsx"],
"project": ["src/**/*.{ts,tsx}"],
"ignore": ["src/legacy/**", "**/*.stories.ts"],
"ignoreDependencies": ["some-cli-tool"]
}
entry: where to start tracing references from
project: which files belong to this project
ignore: paths to skip
ignoreDependencies: dependencies to keep even if they appear unused (e.g. CLI tools)
Auto-Fix
Some issues can be fixed automatically with --fix:
npx knip --fix
Currently --fix handles removing unused entries from package.json and some unused exports. Not everything is auto-fixable, but it saves a lot of manual work on the dependency side.
VSCode Extension and MCP
Knip has a VSCode extension that shows unused exports directly in the editor β no need to run the CLI to find out.
There's also @knip/mcp, which lets AI assistants call Knip when analyzing a project, helping them understand which code is actually in use.
CI Integration Strategy
Wire Knip into the CI pipeline as a blocking step. Run it on every PR to prevent dead code accumulation. Combine with --fix in pre-commit hooks or automated cleanup PRs to maintain a lean dependency graph.
Pitfall Guide
- Misconfigured Entry Points: If
entry does not match the actual framework bootstrap path (e.g., missing pages/ in Next.js or main.ts in Vite), Knip will incorrectly flag entire module branches as dead. Always validate entry patterns against your build toolchain.
- Blindly Executing
--fix: Auto-fix only safely handles package.json cleanup and explicit export removal. Running it without review can strip side-effect imports, framework registrations, or dynamically referenced modules.
- Ignoring Transitive/Implicit Dependencies: Knip flags unlisted dependencies that are implicitly consumed at runtime. Removing them without verifying execution paths or bundler behavior will cause silent runtime failures.
- Over-Broad
ignore Patterns: Using aggressive ignore rules (src/legacy/**, **/*.test.ts) creates a dead code graveyard that bypasses detection entirely. Scope ignores narrowly and audit them quarterly.
- Dynamic Import Blind Spots: String interpolation or conditional imports (
import(\./modules/${name}`)`) break static graph analysis. Knip will flag these as unresolved or unused. Explicitly configure ignore rules or use plugin-specific dynamic import resolvers.
- Type-Only Dependency False Positives:
@types/* packages are often flagged when only used for type-checking. Whitelist them in ignoreDependencies or align with tsconfig.json type-checking scopes to prevent accidental removal.
- Skipping CI Gating: Running Knip manually allows technical debt to re-accumulate. Without a CI enforcement step, dead code compounds silently across merges. Treat it as a non-negotiable quality gate.
Deliverables
- Knip Integration Blueprint: Step-by-step architecture for module graph configuration, CI pipeline gating, and automated cleanup workflows tailored to Vite/Next.js/Monorepo environments.
- Dead Code Elimination Checklist: Pre-flight validation matrix covering entry point verification, dependency audit sequencing, auto-fix review protocols, and periodic CI compliance tracking.
- Configuration Templates: Production-ready
knip.json scaffolds for common frameworks, GitHub Actions workflow snippets with caching and parallel execution, and VSCode/MCP sync configurations for real-time editor feedback.