Knip: An Essential Tool for Code Hygiene

25 / Mar / 2026 by Ashutosh Bhandari 0 comments

Modern software projects grow at a fast pace with new features, hot-fixes, refactors and migration, codebases tend to accumulate dead code, unused dependencies, and files that were forgotten but are still sitting in your source code. As a project grows, most of the time unused code starts piling up. As a result it slows down the build and makes the codebase harder to understand and work with.

That’s where Knip comes in the picture. It’s a free, open-source tool that quietly goes through your project and points out all the code that isn’t being used anywhere. It tells you exactly what part of your code is unused and can be safely removed.

What Knip Does

Knip examines your project structure, starting from one or more entry files that represent your application’s main execution points. It then builds a dependency graph by following import/require statements, configuration references, and tooling setup files. From this graph, Knip determines following things:

Unused files — source files that are not referenced anywhere reachable.

Unused exports — exported functions, variables, types, components, or classes which are  never used.

Unused dependencies — Any modules listed in package.json but not imported anywhere in code.

Unlisted dependencies — imported modules missing from package.json.

Unresolved imports — import paths that don’t resolve to valid files or packages.

Unused devDependencies – modules listed in package.json but not used anywhere in code.

Knip looks deeper into your project than the tree-shaking done by bundlers.

It analyses the internal structure of your codebase to detect unused files, exports, and dependencies that bundlers might miss.

While bundlers mainly focus on removing unused code during the build process, Knip inspects the entire project to identify parts of the code that are no longer being used or referenced.

This helps developers keep their projects cleaner, reduce unnecessary dependencies, and maintain a more organised codebase.

Why Use Knip?

There are clear and practical benefits to adopting Knip:

1. Reduce Technical Debt –
Knip helps you to find unused parts in your code base in an automated way that would otherwise remain hidden. Fixing these increases the maintainability of source code.

2. Improve Performance-
Unused/dead code contributes to longer build times and larger bundles. Trimming it reduces compilation and load times.

3. Better Maintainability-
Keeping only what’s essential makes refactors safer and contributes towards a well maintained code base. Eliminating unused packages can reduce vulnerabilities.

4. Automation Compatibility-
Knip integrates well with CI/CD pipelines to enforce hygiene standards before merges — preventing regressions where unused code slips back in.

Getting Started

Knip is designed to work out of the box with most JavaScript and TypeScript setups.

Installation
Depending on your package manager:

# npm
npm install -D knip typescript @types/node

# yarn
yarn add -D knip typescript @types/node

# pnpm
pnpm add -D knip typescript @types/node

Then add a script to your package.json:

{
  "scripts": {
    "knip": "knip"
  }
}

Or run directly without installation:

npx knip

npm run knip

After installation, Knip will analyse the project and generate a report listing unused files, exports, and dependencies.
A sample Output on CLI will look like below, all the findings separated in different categories.

$ npm run knip

> my-app@1.0.0 knip
> knip

Unused files (2)
src/utils/oldFormatter.ts
src/components/DeprecatedBanner.tsx

Unused exports (5)
src/utils/helpers.ts formatDate
src/utils/helpers.ts convertToCSV
src/api/userService.ts fetchUserById
src/hooks/useWindowSize.ts useWindowSize
src/constants/config.ts DEBUG_MODE

Unused types (2)
src/types/legacy.ts LegacyUserProps
src/types/legacy.ts OldApiResponse

Unused dependencies (3)
lodash
moment
axios

Unused devDependencies (1)
@types/lodash

Unresolved imports (1)
src/screens/HomeScreen.tsx ../../shared/theme ← cannot resolve path

 

What If the Output is Overwhelming?

For large projects, when running knip for the first time,  it might find many issues all at once. In such cases we can limit the number of issues shown by knip analysis and try to fix them subsequently.

npm run knip -- --max-show-issues 5

yarn run knip --max-show-issues 5

 

Customizing Knip

Knip works well with its default settings, but larger or more complex codebases usually require some customization. To tailor its behavior, you can add a configuration file such as knip.json, knip.jsonc, or use a dynamic setup via knip.config.js or knip.config.ts.

Below is an example of a basic configuration for a React native Project:

{
"$schema": "https://unpkg.com/knip@5/schema.json",
"entry": [
"src/index.ts", // main entry file (JS/TS bootstrap)
"src/App.tsx" // root React component
// add more if you have multiple entry points (e.g. storybook, dev entry)
],
"project": [
"src/**/*.{js,ts,tsx}"
// include other folders if needed (e.g. "lib/**", "components/**")
],
"ignore": [
"**/__tests__/**", // test directories
"android/**", // native Android project files
"ios/**" // native iOS project files
// optionally add: "coverage/**", "e2e/**"
],
"ignoreDependencies": [
"@react-native/metro-config", // Metro bundler config
"@react-native/babel-preset" // Babel preset for RN
// add more tooling deps if flagged (e.g. "jest", "babel-*")
]
}

In this setup:

  • Specific entry files like src/index.ts and src/App.tsx are declared explicitly.
  • The project field defines which source files Knip should scan.
  • Test folders and platform-specific directories for Android and iOS are excluded from analysis.
  • Certain dependencies are intentionally ignored because they’re required for build tooling but aren’t referenced directly at runtime.

Below is example of React web App:

{
"$schema": "https://unpkg.com/knip@5/schema.json",

"entry": [
"index.html", // main HTML entry
"src/main.tsx", // Vite entry
"src/index.tsx" // CRA/Webpack entry (optional)
// add more entry points if needed (e.g. admin.tsx, dashboard.tsx)
],

"project": [
"src/**/*.{js,jsx,ts,tsx}"
// include other folders if needed (e.g. "lib/**", "components/**")
],

"ignore": [
"**/*.test.*", // unit tests
"**/__tests__/**", // test folders
"dist/**", // production build (Vite)
"build/**" // production build (CRA/Webpack)
// add more like "coverage/**", ".next/**"
],

"ignoreDependencies": [
"@types/*", // TypeScript types
"eslint", // linting
"prettier", // formatting
"vite", // bundler
"webpack" // bundler
// add more tooling if flagged (jest, vitest, babel, etc.)
]
}
  • Entry points such as index.html and src/main.tsx (or src/index.tsx) are explicitly defined to represent the application bootstrap.
  • The project field specifies all source files within src/ that Knip should analyze for unused exports and dependencies.
  • Test files, test directories, and build output folders like dist/ and build/ are excluded to avoid noise in analysis.
  • Tooling-related dependencies (e.g., bundlers, linters, type definitions) are ignored because they are required for development/build processes but not directly imported in application runtime code.

Conclusion

We often spend a lot of time adding new features to our codebase, but we rarely think about the cost of keeping code that we no longer use. Unused or “dead” code slowly becomes technical debt. It can increase build times, make bundles larger, and make the codebase harder to understand.

If you’re working on a JavaScript project that has been around for a few months, try using Knip. You might be surprised by how much unnecessary code it finds and how quickly your project can become lighter and cleaner.

FOUND THIS USEFUL? SHARE IT

Leave a Reply

Your email address will not be published. Required fields are marked *