Stop shipping unused code. Your bundle (and team) will thank you.
The Problem: Dead Code is Everywhere
Every mature codebase has dead code. That 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
Just run it:
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. But 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"
]
}
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
For React Native projects, you'll need to account for native tooling and development dependencies:
{
"$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
I ran Knip on a production React Native app, and here's what it found:
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:
- Increases bundle size
- Confuses new developers
- Creates maintenance burden
- Shows up 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
Add Knip to your CI pipeline to prevent new dead code from being merged:
# GitHub Actions example
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
Knip can automatically remove some types of unused code:
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
1. Start with --include to Focus
Overwhelmed by results? Focus on one category:
# 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. Use --reporter for Different Outputs
# 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 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. Use:
{
"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!"
- Check if the code is dynamically imported
- Add patterns to
ignorein 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
- Potential security vulnerabilities
- Wasted CI/CD minutes
Knip gives you visibility into what's actually being used in your codebase. Run it once, and you might be shocked by what you find. Run it regularly, and keep your codebase lean and maintainable.
# Try it now
npx knip
Resources
- π Knip Documentation
- π GitHub Repository
- π¬ Discord Community
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
Top comments (0)