Knip: The Ultimate Dead Code Detector for JavaScript & TypeScript Projects
Source: Dev.to
The Problem: Dead Code is Everywhere
Every mature codebase has a dirty secret: dead code.
The utility function someone wrote “just in case.”
The component from a feature that was scrapped.
The npm package installed for a spike that never went anywhere.
ESLint can catch unused variables and imports within a file, but what about:
- Files that are never imported anywhere?
- Exported functions that no one uses?
- Dependencies in
package.jsonyou forgot to remove? - Types and interfaces defined but never referenced?
This is where Knip comes in.
What is Knip?
Knip (Dutch for “cut”) is a powerful static‑analysis tool that finds unused files, dependencies, and exports in your JavaScript/TypeScript projects. Think of it as Marie Kondo for your codebase — if code doesn’t spark joy (or get used), it’s gotta go.
What Knip Detects
| Category | Description |
|---|---|
| 🗂️ Unused Files | Source files that aren’t imported anywhere |
| 📦 Unused Dependencies | Packages in package.json that aren’t used |
| 📤 Unused Exports | Functions, classes, types exported but never imported |
| 🔧 Unused Dev Dependencies | Dev packages that aren’t needed |
| ❓ Unlisted Dependencies | Packages used in code but missing from package.json |
| 🔗 Unresolved Imports | Imports pointing to non‑existent modules |

Getting Started
Installation
# npm
npm install -D knip
# yarn
yarn add -D knip
# pnpm
pnpm add -D knip
Basic Usage
npx knip
That’s it! Knip will analyze your project and output all the unused code it finds.
Configuration
For most projects, Knip works out of the box. For complex setups (monorepos, custom entry points, etc.) you’ll want a config file.
Create knip.json in your project root:
{
"$schema": "https://unpkg.com/knip@5/schema.json",
"entry": ["src/index.ts", "src/App.tsx"],
"project": ["src/**/*.{ts,tsx}"],
"ignore": [
"**/__tests__/**",
"**/__mocks__/**",
"**/node_modules/**"
],
"ignoreDependencies": [
"prettier",
"husky"
],
"ignoreExportsUsedInFile": true
}
Key Configuration Options
| Option | Description |
|---|---|
entry | Entry‑point files where Knip starts tracing |
project | Files to analyze |
ignore | Files/patterns to skip |
ignoreDependencies | Dependencies to skip (useful for config‑only packages) |
ignoreExportsUsedInFile | Don’t report exports used only in the same file |
React Native Example
{
"$schema": "https://unpkg.com/knip@5/schema.json",
"entry": ["src/App.tsx", "index.js"],
"project": ["src/**/*.{ts,tsx}"],
"ignore": [
"**/__tests__/**",
"**/__mocks__/**",
"android/**",
"ios/**"
],
"ignoreDependencies": [
"@react-native/metro-config",
"@react-native/typescript-config",
"@react-native/babel-preset",
"patch-package",
"husky",
"reactotron-react-native"
],
"ignoreExportsUsedInFile": true
}
Real‑World Results
Running Knip on a production React Native app produced:
Unused files (73)
src/components/map/index.tsx
src/components/views/NotificationMenuButton.tsx
src/models/requests/auth/authRequests.ts
src/utils/helpers/contactHelper.ts
... and 69 more
Unused dependencies (1)
@react-native-firebase/perf
Unused devDependencies (4)
@babel/preset-env
@testing-library/jest-native
@types/react-native-get-random-values
eslint-plugin-react-you-might-not-need-an-effect
Unlisted dependencies (2)
credit-card-type src/features/paymentMethods/addNewCard/index.tsx
Unused exports (74)
translations src/config/localization/languages.ts
defaultAddressForm src/features/addressBook/addressTypes.ts
... and more
73 unused files! That’s potentially thousands of lines of dead code that:
- Increase bundle size
- Confuse new developers
- Create maintenance burden
- Appear in search results, wasting time
Integrating with Your Workflow
NPM Scripts
Add these to your package.json:
{
"scripts": {
"knip:check": "knip",
"knip:fix": "knip --fix"
}
}
CI/CD Integration
# .github/workflows/code-quality.yml
name: Code Quality
on: [push, pull_request]
jobs:
knip:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npx knip
Pre‑commit Hook (with Husky)
# .husky/pre-commit
npx knip --no-exit-code
Auto‑Fixing with --fix
npx knip --fix
⚠️ Warning: This modifies your files! Always review changes and have version control ready.
What --fix can do:
- ✅ Remove unused exports
- ✅ Remove unused dependencies from
package.json - ❌ Cannot delete unused files (too risky)
Tips & Best Practices
- Start with
--includeto focus – If you’re overwhelmed by results, limit the analysis to a specific folder or category first. - Run Knip regularly (e.g., in CI) to keep dead code from accumulating.
- Pair Knip with code‑review checklists: “No new unused imports/files.”
- After fixing, commit the changes and let the team know the cleanup is complete.
By adopting Knip, you’ll keep your codebase lean, improve developer experience, and ship smaller, faster bundles. Happy cleaning!
Usage Tips for Knip
1. Include Specific Checks
# Only check for unused files
npx knip --include files
# Only check dependencies
npx knip --include dependencies
# Check multiple categories
npx knip --include files,exports
2. Choose an Output Reporter
# JSON output (great for tooling)
npx knip --reporter json
# Markdown (for documentation)
npx knip --reporter markdown
3. Ignore Known False Positives
Some code is used dynamically or through conventions that Knip can’t detect:
{
"ignore": [
"src/generated/**",
"**/*.stories.tsx"
],
"ignoreDependencies": [
"tsconfig-paths"
]
}
4. Handle Barrel Exports
If you use barrel files (index.ts re‑exporting everything), Knip might report false positives. Enable:
{
"ignoreExportsUsedInFile": true
}
5. Framework‑Specific Plugins
Knip has built‑in support for many frameworks:
{
"next": {
"entry": ["pages/**/*.tsx", "app/**/*.tsx"]
},
"jest": {
"config": ["jest.config.js"]
},
"storybook": {
"entry": [".storybook/main.ts"]
}
}
Knip vs. Other Tools
| Tool | Unused Files | Unused Exports | Unused Deps | Auto‑fix |
|---|---|---|---|---|
| Knip | ✅ | ✅ | ✅ | ✅ |
| ESLint | ❌ | ❌ | ❌ | Partial |
| ts‑prune | ❌ | ✅ | ❌ | ❌ |
| depcheck | ❌ | ❌ | ✅ | ❌ |
| unimported | ✅ | ❌ | ✅ | ❌ |
Knip is the all‑in‑one solution that replaces multiple tools.
Common Issues & Solutions
“I’m getting too many false positives!”
- Verify if the code is dynamically imported.
- Add patterns to
ignorein the config. - Use
ignoreDependenciesfor config‑only packages. - Enable
ignoreExportsUsedInFile.
“Knip is slow on my large monorepo”
{
"ignore": [
"**/dist/**",
"**/build/**",
"**/coverage/**"
]
}
“It’s not finding my entry points”
Explicitly define them:
{
"entry": [
"src/index.ts",
"src/cli.ts",
"scripts/*.ts"
]
}
Conclusion
Dead code is technical debt that compounds over time. Every unused file is:
- Bytes shipped to users
- Cognitive overhead for developers
- A potential security vulnerability
- Wasted CI/CD minutes
Knip gives you visibility into what’s actually being used in your codebase. Run it once—you might be shocked by what you find. Run it regularly, and keep your codebase lean and maintainable.
# Try it now
npx knip
Resources
Did Knip help clean up your codebase? Share your results in the comments! I’d love to hear how much dead code you found. 🔪
Tags: javascript, typescript, webdev, productivity