Declutter Your JavaScript and TypeScript Projects.

Hiee! Welcome to another post where I’m going to show you how we can use Knip to declutter our TypeScript and JavaScript projects.

What is Knip?

Knip is an amazing tool that aims to declutter your JavaScript and TypeScript projects. But what does decluttering mean? If you’ve ever worked on a project for a while that involves a bunch of people or even just yourself, you’ll notice that unused code and dependencies tend to accumulate in the codebase over time. This is also known as Technical Debt.

What is Technical Debt?

Technical Debt is the mess no one on your team wants to touch. Oh wait, that sounds a bit too vague. Here’s a better one: Technical Debt can be anything from legacy code that might just be hanging by a thread or might be craving for a refactor, or it could be unused code, dependencies, config files, and more.

A very practical example of how this technical debt builds up: we often remove features from our projects due to low traction, but we forget to clean up all the related code. Or we over-engineer solutions far too often, which leaves behind unused abstractions and helper functions.

This is even more relevant today because we blindly accept changes that an LLM makes to our codebase these days. Since most LLMs are trained on shitty code, you should always expect them to do the bare minimum and not the other way around. Which means even more unused & irrelevant code gets left behind.

Running Knip

Well, enough of what is what, and let’s see how we can actually clean that mess up. This is where Knip comes into play. When you run Knip on your codebase, it gives you a nice summary of which files, components, exported functions, constants, etc. are currently unused in your codebase.

This is the moment you will realize how much of the mess has built up over time that went unnoticed. Unless you stick to extremely strict code standards and reviews, preventing this is close to impossible because the human brain can only keep track of so much.

Here’s an example Knip summary from one of the projects that I ran Knip on: $ pnpx knip

Unused files (5)
app/components/cart.tsx
app/components/emoji-picker.tsx
app/components/media-dropzone.tsx
app/components/profile.tsx
app/components/select-image-modal.tsx

Unused dependencies (4)
@axiomhq/pino                              package.json:32:6
@hono/zod-validator                        package.json:36:6
@opentelemetry/auto-instrumentations-node  package.json:41:6
@tailwindcss/typography                    package.json:63:6

Unused exports (4)
CreatePost                             function  app/components/create-post.tsx:51:17
HappeningNowModal                      function  app/components/happening-now.tsx:755:17
ShareButton                            function  app/components/post.tsx:141:17
StickerPopover                         function  app/components/post.tsx:193:17
TipPostPopoverItem                     function  app/components/post.tsx:549:17

Unused exported enum members (5)
GUARANTEED  DistributionStrategy       app/enums.ts:41:3
CATEGORY    SanitySearchKeys           app/enums.ts:67:3
SLUG        SanitySearchKeys           app/enums.ts:68:3
TITLE       SanitySearchKeys           app/enums.ts:69:3
STICKERS    FocusCarouselResourceType  app/types.ts:526:3

Wow! All we did was run a single command, and now we can see everything that is not being used in our codebase. How great is that? Honestly, this is one of the best parts of Knip. It is almost config-less. Here’s a quote from its docs: Knip has good defaults and aims for “zero config.”

This might be hard to believe, but I was able to delete 12k+ lines of code across 100+ files using Knip, which I would not have been able to do otherwise by myself.

Using A Config File

But since every project is different, where you might want to run Knip can also change. You may want to run it on a fraction of a codebase. This is where the config file comes in. Here’s an example config file from the docs: knip.json

{
  "$schema": "https://unpkg.com/knip@5/schema.json",
  "entry": ["src/index.ts", "scripts/{build,create}.js"],
  "project": ["src/**/*.ts", "scripts/**/*.js"]
}

As you can see, you can customize where Knip runs, what it lints, etc. It has a lot of other config options too, and honestly, it would be too much for this post to cover all those. But these are the bare minimum and most likely suitable for the vast majority of projects. But if you really wanna get into the details, I suggest you follow the docs.

Anyway, coming back to the summary that Knip gave us, what can we do here? For starters, you can just go to those files and remove all the suggested unused stuff manually. But wait, this doesn’t sound like a very easy task, right? Yeah, because it isn’t. It’s pretty tough to go to every single file and delete things.

But don’t worry, Knip has got us covered there too. Knip provides us with an auto-fixer knip --fix --allow-remove-files flag that goes ahead and removes the suggested deletions for you. Can you believe this tool is free? I can’t, but here we are.

Alright, that sounds a bit dangerous, right? Letting a tool do file operations on our codebase? And yes, it should sound dangerous because it is, but what is git there for? As long as you check out to a new branch, you can do whatever you want with the copy of your codebase. You’ll only need to worry if you’re not using git, which I don’t think is even possible in 2025.

The Next Steps

Great! We did everything we were supposed to do. Now all we need to do is review the changes that Knip made. But why do we need to review? Well, it is to make sure we did not accidentally remove anything that was being used. It is always good to check twice so that we don’t end up breaking prod on a friday evening.

One of my favorite things about all of this is, if you’re using TypeScript, you can just run the TypeScript typechecker tsc --noEmit, which will tell you if you accidentally removed anything that was being used somewhere. But if it doesn’t complain, you have nothing to worry about. Go ahead and merge that PR. But if you’re using JavaScript, first of all, I’m sorry you had to maintain a JS codebase; secondly, you can still probably get away with using a linter, so it should be fine.

Alright, that’s it for this post. Hope you learned something. Run Knip on your codebase today, and you will thank yourself later. Happy deleting code.

Takeaways

  • While this may seem as simple as running a single command, it is not a do once and forget kind of thing. You should probably add this to your dev scripts or CI and make sure this runs frequently so you can keep clearing out the stuff as it comes.
  • Do not blindly accept changes from LLMs. They often over-engineer and add unnecessary things that are not even relevant to that particular task.
  • A lot of devs don’t care about tech debt, but try being that person who cares—you will thank yourself later for taking that responsibility.