DEV Community

Ajmal Hasan
Ajmal Hasan

Posted on

Knip: Dead Code Detector for JavaScript & TypeScript Projects

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.json you 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
Enter fullscreen mode Exit fullscreen mode

Basic Usage

Just run it:

npx knip
Enter fullscreen mode Exit fullscreen mode

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"
  ]
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"
  }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Pre-commit Hook (with Husky)

# .husky/pre-commit
npx knip --no-exit-code
Enter fullscreen mode Exit fullscreen mode

Auto-Fixing with --fix

Knip can automatically remove some types of unused code:

npx knip --fix
Enter fullscreen mode Exit fullscreen mode

⚠️ 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
Enter fullscreen mode Exit fullscreen mode

2. Use --reporter for Different Outputs

# JSON output (great for tooling)
npx knip --reporter json

# Markdown (for documentation)
npx knip --reporter markdown
Enter fullscreen mode Exit fullscreen mode

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"
  ]
}
Enter fullscreen mode Exit fullscreen mode

4. Handle Barrel Exports

If you use barrel files (index.ts re-exporting everything), Knip might report false positives. Use:

{
  "ignoreExportsUsedInFile": true
}
Enter fullscreen mode Exit fullscreen mode

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"]
  }
}
Enter fullscreen mode Exit fullscreen mode

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!"

  1. Check if the code is dynamically imported
  2. Add patterns to ignore in config
  3. Use ignoreDependencies for config-only packages
  4. Enable ignoreExportsUsedInFile

"Knip is slow on my large monorepo"

{
  "ignore": [
    "**/dist/**",
    "**/build/**",
    "**/coverage/**"
  ]
}
Enter fullscreen mode Exit fullscreen mode

"It's not finding my entry points"

Explicitly define them:

{
  "entry": [
    "src/index.ts",
    "src/cli.ts",
    "scripts/*.ts"
  ]
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)