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
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
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:
- Framework-aware: It understands Angular modules (though we use standalone now), React components, Next.js pages, and more
- Configurable: You can tell Knip about custom entry points or ignore certain patterns
- 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
Running Knip:
npx knip
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
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/*"]
}
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
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`);
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"]
}
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"]
}
}
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' });
});
});
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:
- Run Knip monthly: Set a calendar reminder to audit your codebase
- Review before merging: Make Knip checks part of your PR review process
- Delete aggressively: If code is unused for 3+ months, it is probably safe to remove
- Use feature flags: Instead of commenting out code, use feature flags and delete the code when the flag is removed
- 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:
-
Day 1: Run
npx knipon your project and review the report -
Day 2: Create a
knip.jsonconfig to tune the results - Day 3: Remove obviously unused files and dependencies
- Day 4: Run your test suite to verify nothing broke
- 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 knipin your project root -
Configuration: Customize Knip using
knip.jsonto 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! 🧪🧠🚀
Top comments (0)