knip-dead-code
Run Knip against a JS/TS project to detect unused files, unused dependencies, unused exports, and unused class/enum members. Scoped to production code; tests are entry-point-aware via Knip's framework plugins.
knip-dead-code
Knip detects unused files, dependencies, exports, types, enum members, and class members in JS/TS projects per the Knip docs. Requires Node ≥ 20.19.0 (or Bun).
When to use
Step 1 - Install
# Recommended: setup wizard (auto-detects framework)
npm init @knip/config
# Manual
npm install -D knip typescript @types/nodeAdd package.json script:
{
"scripts": {
"knip": "knip"
}
}Per the Knip docs.
Step 2 - Configure entry points
knip.json:
{
"$schema": "https://unpkg.com/knip@5/schema.json",
"entry": [
"src/index.ts",
"src/cli.ts",
"scripts/**/*.ts"
],
"project": ["src/**/*.{ts,tsx}"],
"ignore": ["src/**/*.generated.ts"],
"ignoreDependencies": [
"@types/node"
],
"ignoreExportsUsedInFile": true
}Or knip.config.ts for typed config.
Entry vs project: entry = files Knip starts walking the import graph from. project = the universe of files it considers reachable. Anything in project not reached from entry is unused.
Step 3 - First run
npx knipExit code: 0 if clean, non-zero if unused items found. Shows:
Unused files (3)
Unused dependencies (2)
Unused exports (12)
Unused exported types (4)
Unused exported enum members (1)Step 4 - Use framework plugins
For framework-aware scanning, enable plugins (auto-detected by npm init @knip/config):
{
"next": {
"entry": ["next.config.{js,ts,cjs,mjs}", "app/**/page.{js,ts,tsx}"]
},
"playwright": {
"config": "playwright.config.ts",
"entry": ["e2e/**/*.spec.ts"]
}
}Plugin list (Next.js, Remix, Astro, Vite, Storybook, Playwright, Jest, etc.) - see the Knip plugins page.
Step 5 - Limit output during cleanup
# Show only top 5 of each issue type — useful when first adopting
npx knip --max-show-issues 5Per the Knip docs for overwhelming output.
Step 6 - CI gate
# GitHub Actions
- name: Knip dead-code check
run: npx knipFails build on any new dead code. For brownfield rollout, baseline existing dead code first:
# Snapshot current findings; CI only fails on additions
npx knip --no-exit-code > knip-baseline.txtStep 7 - Per-issue triage
| Reported as | True positive? | Action |
|---|---|---|
| Unused file | Usually yes | Delete |
| Unused dependency | Usually yes | npm uninstall |
| Unused export | Sometimes (public API) | Mark with JSDoc @public + tags: ["public"] config |
| Unused enum member | Usually yes | Delete |
| Unused class member | Sometimes (public API) | Same as unused export |
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Run without entry points | Everything looks orphaned | Always set entry (Step 2) |
| Treat "unused export" as "delete now" | Public library APIs flag as unused | Use tags: ["public"] for library packages |
| Skip framework plugins | Next.js page files look orphaned (Knip doesn't know they're routes) | Enable plugin (Step 4) |
| Block PRs from day 1 in brownfield | Hundreds of pre-existing unused; team disables tool | Baseline first (Step 6 alt path) |
| Auto-fix mode on first run | --fix deletes legitimate unused public exports | Manual review first; --fix after baseline clean |