DEV Community

Cover image for npx knip: The Smart Way to Detect Dead Code in Your JavaScript & TypeScript Projects
Rajat
Rajat

Posted on

npx knip: The Smart Way to Detect Dead Code in Your JavaScript & TypeScript Projects

Discover how one command can clean up thousands of lines of unused code and boost your codebase health


Have you ever wondered how much of your codebase is actually being used? If you are maintaining a medium to large application, chances are you have accumulated dead code, unused dependencies, and orphaned files that nobody dares to touch. What if I told you there is a tool that can find all of this automatically in seconds?

In this article, you will learn how to use npx knip to detect and eliminate dead code, unused exports, redundant dependencies, and more—all without installing anything permanently. By the end, you will have a clear action plan to clean up your JavaScript or TypeScript project and keep it lean going forward.

Let me ask you this: When was the last time you audited your dependencies or checked for unused exports? If the answer is "never" or "I don't remember," keep reading.


What is Knip and Why Should You Care?

Knip is a comprehensive tool that analyzes your JavaScript and TypeScript projects to find:

  • Unused files that are no longer imported anywhere
  • Unused dependencies sitting in your package.json
  • Unused exports from modules
  • Unused types in TypeScript files
  • Unreachable code and much more

Think of Knip as a linter for your entire project structure, not just your code syntax. It works with Angular, React, Next.js, Vue, Node.js, and virtually any JavaScript ecosystem project.

The best part? You can run it with npx knip without installing it globally. That means zero setup friction.


💬 Quick question: Have you ever deleted a component or service only to realize later that its imports are still scattered across your codebase? Drop a comment below—I would love to hear your cleanup horror stories.


Getting Started with Knip

Let's dive straight into action. Here is how you run Knip on any project.

Step 1: Run Knip with npx

Navigate to your project root and run:

npx knip

Enter fullscreen mode Exit fullscreen mode

That is it. Knip will analyze your project and show you a report of unused files, dependencies, and exports.

Sample Output:

Unused files (3)
  src/utils/old-helper.ts
  src/components/legacy-modal.component.ts
  src/services/deprecated-api.service.ts

Unused dependencies (5)
  lodash
  moment
  uuid
  axios
  rxjs-compat

Unused exports (12)
  calculateTax in src/utils/math.ts
  parseDate in src/utils/date-parser.ts
  LegacyUser in src/models/user.model.ts

Enter fullscreen mode Exit fullscreen mode

This output immediately tells you what is safe to remove. But before you start deleting, let's understand how Knip works.


How Knip Works Under the Hood

Knip uses static analysis to trace your codebase. It starts from your entry points (like main.ts, app.component.ts, or index.ts) and follows all imports to build a dependency graph.

Anything not reachable from these entry points gets flagged as unused. Here is what makes Knip smart:

  1. Framework-aware: It understands Angular modules (though we use standalone now), React components, Next.js pages, and more
  2. Configurable: You can tell Knip about custom entry points or ignore certain patterns
  3. Fast: It analyzes thousands of files in seconds

Real-World Example: Cleaning Up an Angular Project

Let's say you have an Angular 18 application using standalone components. Over time, you have accumulated unused services, old components, and forgotten utility functions. Here is how Knip helps.

Project Structure Before Cleanup:

src/
├── app/
│   ├── components/
│   │   ├── user-list.component.ts
│   │   ├── user-detail.component.ts
│   │   └── old-dashboard.component.ts  // unused
│   ├── services/
│   │   ├── user.service.ts
│   │   ├── auth.service.ts
│   │   └── legacy-api.service.ts  // unused
│   └── utils/
│       ├── format.ts
│       └── old-helpers.ts  // unused
└── main.ts

Enter fullscreen mode Exit fullscreen mode

Running Knip:

npx knip

Enter fullscreen mode Exit fullscreen mode

Knip Output:

Unused files (2)
  src/app/components/old-dashboard.component.ts
  src/app/services/legacy-api.service.ts

Unused exports (3)
  oldHelperFunction in src/app/utils/old-helpers.ts
  formatCurrency in src/app/utils/format.ts
  deprecatedMethod in src/app/services/user.service.ts

Enter fullscreen mode Exit fullscreen mode

Now you know exactly what to remove. But wait—what if Knip is wrong?


Configuring Knip for Your Project

Sometimes Knip flags code that looks unused but is actually needed. For example:

  • Entry points that Knip doesn't know about
  • Dynamic imports
  • Code used only in tests

You can configure Knip using a knip.json or knip.ts file.

Example Configuration:

{
  "entry": ["src/main.ts", "src/polyfills.ts"],
  "project": ["src/**/*.ts"],
  "ignore": ["src/**/*.spec.ts", "src/environments/**"],
  "ignoreDependencies": ["@types/*"]
}

Enter fullscreen mode Exit fullscreen mode

This tells Knip:

  • Where your entry points are
  • Which files to analyze
  • What to ignore (like test files)
  • Which dependencies to skip (like type definitions)

👏 If this is making sense so far, hit that clap button—it helps other devs discover this too.


Integrating Knip into Your CI/CD Pipeline

Once you have cleaned up your codebase, you want to keep it clean. The best way? Run Knip in your CI pipeline.

GitHub Actions Example:

name: Code Health Check

on: [push, pull_request]

jobs:
  knip:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: 18
      - run: npm install
      - run: npx knip

Enter fullscreen mode Exit fullscreen mode

Now every pull request gets checked for dead code automatically. If someone adds unused code, the build fails. Simple and effective.


Handling Common Knip Warnings

Let's address some scenarios you might encounter:

1. False Positives with Dynamic Imports

If you use dynamic imports like this:

const moduleName = 'user-dashboard';
import(`./modules/${moduleName}.component`);

Enter fullscreen mode Exit fullscreen mode

Knip might not detect these. Solution: Add them to your entry points or use the ignore configuration.

2. Unused Dependencies That Are Actually Needed

Some packages like @angular/animations are needed at runtime but might not show up in your imports. Use ignoreDependencies for these:

{
  "ignoreDependencies": ["@angular/animations", "zone.js"]
}

Enter fullscreen mode Exit fullscreen mode

3. Files Only Used in Production

If certain files are only used in specific environments, you can create environment-specific configurations:

{
  "production": {
    "entry": ["src/main.ts", "src/production-setup.ts"]
  }
}

Enter fullscreen mode Exit fullscreen mode

Testing Your Cleanup with Unit Tests

After removing dead code, you should verify nothing broke. Here is how to write a quick smoke test for an Angular service:

Example: Testing User Service After Cleanup

import { TestBed } from '@angular/core/testing';
import { UserService } from './user.service';
import { provideHttpClient } from '@angular/common/http';
import { provideHttpClientTesting, HttpTestingController } from '@angular/common/http/testing';

describe('UserService', () => {
  let service: UserService;
  let httpMock: HttpTestingController;

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        UserService,
        provideHttpClient(),
        provideHttpClientTesting()
      ]
    });

    service = TestBed.inject(UserService);
    httpMock = TestBed.inject(HttpTestingController);
  });

  afterEach(() => {
    httpMock.verify();
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  it('should fetch users successfully', () => {
    const mockUsers = [
      { id: 1, name: 'John' },
      { id: 2, name: 'Jane' }
    ];

    service.getUsers().subscribe(users => {
      expect(users).toEqual(mockUsers);
      expect(users.length).toBe(2);
    });

    const req = httpMock.expectOne('api/users');
    expect(req.request.method).toBe('GET');
    req.flush(mockUsers);
  });

  it('should handle errors gracefully', () => {
    service.getUsers().subscribe({
      next: () => fail('should have failed'),
      error: (error) => {
        expect(error.status).toBe(500);
      }
    });

    const req = httpMock.expectOne('api/users');
    req.flush('Server error', { status: 500, statusText: 'Server Error' });
  });
});

Enter fullscreen mode Exit fullscreen mode

This ensures your service still works after removing unused methods or dependencies.


💡 Pro tip incoming: Run Knip before and after major refactorings. It is like a safety net that catches orphaned code immediately.


Bonus Tips for Keeping Your Codebase Clean

Here are some practices I follow to prevent dead code accumulation:

  1. Run Knip monthly: Set a calendar reminder to audit your codebase
  2. Review before merging: Make Knip checks part of your PR review process
  3. Delete aggressively: If code is unused for 3+ months, it is probably safe to remove
  4. Use feature flags: Instead of commenting out code, use feature flags and delete the code when the flag is removed
  5. Document why code exists: If something looks unused but isn't, add a comment explaining why

Comparing Knip to Other Tools

You might be wondering: "How is Knip different from ESLint or Depcheck?"

Tool What It Finds Speed Configuration
Knip Unused files, exports, dependencies, types Fast Moderate
ESLint Code quality issues, unused variables Fast High
Depcheck Unused dependencies only Fast Low
Webpack Bundle Analyzer Bundle size, what's included Slow Low

Knip is the most comprehensive for finding structural waste. Use it alongside ESLint for the best results.


Your Action Plan: Clean Up This Week

Here is what you can do right now:

  1. Day 1: Run npx knip on your project and review the report
  2. Day 2: Create a knip.json config to tune the results
  3. Day 3: Remove obviously unused files and dependencies
  4. Day 4: Run your test suite to verify nothing broke
  5. Day 5: Add Knip to your CI pipeline

By Friday, you will have a leaner, cleaner codebase.


Recap: What We Covered

Let's wrap up what you learned today:

  • What Knip is: A static analysis tool for finding dead code in JavaScript and TypeScript projects
  • How to use it: Simply run npx knip in your project root
  • Configuration: Customize Knip using knip.json to fit your project structure
  • CI Integration: Add Knip to your pipeline to prevent future bloat
  • Testing strategy: Write unit tests to verify your cleanup didn't break anything
  • Best practices: Run Knip regularly and delete unused code aggressively

The key takeaway? Dead code is not just clutter—it slows down builds, confuses developers, and increases maintenance burden. Knip makes cleaning it up trivial.


Let's Keep the Conversation Going

👇 I want to hear from you:

💬 What percentage of your codebase do you think is unused? Drop your guess in the comments—I am curious to see the range.

👏 If this saved you even 10 minutes of manual searching, smash that clap button. It helps other developers discover this tool too.

📬 Want more practical dev tips like this? Follow me for weekly insights on Angular, TypeScript, and modern frontend development. I share one actionable tip every week.

🔥 Challenge: Run Knip on your project this week and share your results. Tag me with your findings—I will feature the most interesting cleanups in my next article.


🎯 Your Turn, Devs!

👀 Did this article spark new ideas or help solve a real problem?

💬 I'd love to hear about it!

✅ Are you already using this technique in your Angular or frontend project?

🧠 Got questions, doubts, or your own twist on the approach?

Drop them in the comments below — let’s learn together!


🙌 Let’s Grow Together!

If this article added value to your dev journey:

🔁 Share it with your team, tech friends, or community — you never know who might need it right now.

📌 Save it for later and revisit as a quick reference.


🚀 Follow Me for More Angular & Frontend Goodness:

I regularly share hands-on tutorials, clean code tips, scalable frontend architecture, and real-world problem-solving guides.

  • 💼 LinkedIn — Let’s connect professionally
  • 🎥 Threads — Short-form frontend insights
  • 🐦 X (Twitter) — Developer banter + code snippets
  • 👥 BlueSky — Stay up to date on frontend trends
  • 🌟 GitHub Projects — Explore code in action
  • 🌐 Website — Everything in one place
  • 📚 Medium Blog — Long-form content and deep-dives
  • 💬 Dev Blog — Free Long-form content and deep-dives
  • ✉️ Substack — Weekly frontend stories & curated resources
  • 🧩 Portfolio — Projects, talks, and recognitions
  • ✍️ Hashnode — Developer blog posts & tech discussions

🎉 If you found this article valuable:

  • Leave a 👏 Clap
  • Drop a 💬 Comment
  • Hit 🔔 Follow for more weekly frontend insights

Let’s build cleaner, faster, and smarter web apps — together.

Stay tuned for more Angular tips, patterns, and performance tricks! 🧪🧠🚀

✨ Share Your Thoughts To 📣 Set Your Notification Preference

Top comments (0)