{"id":78930,"date":"2026-03-25T18:01:15","date_gmt":"2026-03-25T12:31:15","guid":{"rendered":"https:\/\/www.tothenew.com\/blog\/?p=78930"},"modified":"2026-03-25T20:58:20","modified_gmt":"2026-03-25T15:28:20","slug":"knip-an-essential-tool-for-code-hygiene","status":"publish","type":"post","link":"https:\/\/www.tothenew.com\/blog\/knip-an-essential-tool-for-code-hygiene\/","title":{"rendered":"Knip: An Essential Tool for Code Hygiene"},"content":{"rendered":"<p>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.<\/p>\n<p>That&#8217;s where Knip comes in the picture. It&#8217;s a free, open-source tool that quietly goes through your project and points out all the code that isn&#8217;t being used anywhere. It tells you exactly what part of your code is unused and can be safely removed.<\/p>\n<h2><strong>What Knip Does<\/strong><\/h2>\n<p>Knip examines your project structure, starting from one or more entry files that represent your application\u2019s 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:<\/p>\n<p><strong>Unused files<\/strong> \u2014 source files that are not referenced anywhere reachable.<\/p>\n<p><strong>Unused exports<\/strong> \u2014 exported functions, variables, types, components, or classes which are\u00a0 never used.<\/p>\n<p><strong>Unused dependencies<\/strong> \u2014 Any modules listed in package.json but not imported anywhere in code.<\/p>\n<p><strong>Unlisted dependencies<\/strong> \u2014 imported modules missing from package.json.<\/p>\n<p><strong>Unresolved imports <\/strong>\u2014 import paths that don\u2019t resolve to valid files or packages.<\/p>\n<p><strong>Unused devDependencies<\/strong> &#8211; modules listed in package.json but not used anywhere in code.<\/p>\n<p>Knip looks deeper into your project than the tree-shaking done by bundlers.<\/p>\n<p>It analyses the internal structure of your codebase to detect unused files, exports, and dependencies that bundlers might miss.<\/p>\n<p>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.<\/p>\n<p>This helps developers keep their projects cleaner, reduce unnecessary dependencies, and maintain a more organised codebase.<\/p>\n<h2><\/h2>\n<h2>Why Use Knip?<\/h2>\n<p>There are clear and practical benefits to adopting Knip:<\/p>\n<p>1. <strong>Reduce Technical Debt &#8211;<\/strong><br \/>\nKnip 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.<\/p>\n<p>2. <strong>Improve Performance-<\/strong><br \/>\nUnused\/dead code contributes to longer build times and larger bundles. Trimming it reduces compilation and load times.<\/p>\n<p>3. <strong>Better Maintainability-<\/strong><br \/>\nKeeping only what\u2019s essential makes refactors safer and contributes towards a well maintained code base. Eliminating unused packages can reduce vulnerabilities.<\/p>\n<p>4. <strong>Automation Compatibility-<\/strong><br \/>\nKnip integrates well with CI\/CD pipelines to enforce hygiene standards before merges \u2014 preventing regressions where unused code slips back in.<\/p>\n<h2><\/h2>\n<h2>Getting Started<\/h2>\n<p>Knip is designed to work out of the box with most JavaScript and TypeScript setups.<\/p>\n<p>Installation<br \/>\nDepending on your package manager:<\/p>\n<pre><span style=\"color: #008000;\"># npm\r\nnpm install -D knip typescript @types\/node\r\n\r\n# yarn\r\nyarn add -D knip typescript @types\/node\r\n\r\n# pnpm\r\npnpm add -D knip typescript @types\/node<\/span><\/pre>\n<p>Then add a script to your package.json:<\/p>\n<pre><span style=\"color: #008000;\">{\r\n \u00a0\"scripts\": {\r\n \u00a0\u00a0\u00a0\"knip\": \"knip\"\r\n \u00a0}\r\n}<\/span><\/pre>\n<p>Or run directly without installation:<\/p>\n<pre><span style=\"color: #008000;\">npx knip\r\n\r\nnpm run knip<\/span><\/pre>\n<p>After installation, Knip will analyse the project and generate a report listing unused files, exports, and dependencies.<br \/>\nA sample Output on CLI will look like below, all the findings separated in different categories.<\/p>\n<pre><span style=\"color: #008000;\">$ npm run knip<\/span>\r\n\r\n<span style=\"color: #008000;\">&gt; my-app@1.0.0 knip<\/span>\r\n<span style=\"color: #008000;\">&gt; knip<\/span>\r\n\r\n<span style=\"color: #008000;\">Unused files (2)<\/span>\r\n<span style=\"color: #008000;\">src\/utils\/oldFormatter.ts<\/span>\r\n<span style=\"color: #008000;\">src\/components\/DeprecatedBanner.tsx<\/span>\r\n\r\n<span style=\"color: #008000;\">Unused exports (5)<\/span>\r\n<span style=\"color: #008000;\">src\/utils\/helpers.ts formatDate<\/span>\r\n<span style=\"color: #008000;\">src\/utils\/helpers.ts convertToCSV<\/span>\r\n<span style=\"color: #008000;\">src\/api\/userService.ts fetchUserById<\/span>\r\n<span style=\"color: #008000;\">src\/hooks\/useWindowSize.ts useWindowSize<\/span>\r\n<span style=\"color: #008000;\">src\/constants\/config.ts DEBUG_MODE<\/span>\r\n\r\n<span style=\"color: #008000;\">Unused types (2)<\/span>\r\n<span style=\"color: #008000;\">src\/types\/legacy.ts LegacyUserProps<\/span>\r\n<span style=\"color: #008000;\">src\/types\/legacy.ts OldApiResponse<\/span>\r\n\r\n<span style=\"color: #008000;\">Unused dependencies (3)<\/span>\r\n<span style=\"color: #008000;\">lodash<\/span>\r\n<span style=\"color: #008000;\">moment<\/span>\r\n<span style=\"color: #008000;\">axios<\/span>\r\n\r\n<span style=\"color: #008000;\">Unused devDependencies (1)<\/span>\r\n<span style=\"color: #008000;\">@types\/lodash<\/span>\r\n\r\n<span style=\"color: #008000;\">Unresolved imports (1)<\/span>\r\n<span style=\"color: #008000;\">src\/screens\/HomeScreen.tsx ..\/..\/shared\/theme \u2190 cannot resolve path<\/span>\r\n\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p><strong>What If the Output is Overwhelming?<\/strong><\/p>\n<p>For large projects, when running knip for the first time,\u00a0 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.<\/p>\n<pre><span style=\"color: #008000;\">npm run knip -- --max-show-issues 5\r\n\r\nyarn run knip --max-show-issues 5<\/span><\/pre>\n<p>&nbsp;<\/p>\n<h2>Customizing Knip<\/h2>\n<p>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 <span style=\"color: #008000;\">knip.json<\/span>, <span style=\"color: #008000;\">knip.jsonc<\/span>, or use a dynamic setup via <span style=\"color: #008000;\">knip.config.js<\/span> or <span style=\"color: #008000;\">knip.config.ts<\/span>.<\/p>\n<p>Below is an example of a basic configuration for a <strong>React native Project<\/strong>:<\/p>\n<pre><span style=\"color: #008000;\">{<\/span>\r\n<span style=\"color: #008000;\">\"$schema\": \"https:\/\/unpkg.com\/knip@5\/schema.json\",<\/span>\r\n<span style=\"color: #008000;\">\"entry\": [<\/span>\r\n<span style=\"color: #008000;\">\"src\/index.ts\", \/\/ main entry file (JS\/TS bootstrap)<\/span>\r\n<span style=\"color: #008000;\">\"src\/App.tsx\" \/\/ root React component<\/span>\r\n<span style=\"color: #008000;\">\/\/ add more if you have multiple entry points (e.g. storybook, dev entry)<\/span>\r\n<span style=\"color: #008000;\">],<\/span>\r\n<span style=\"color: #008000;\">\"project\": [<\/span>\r\n<span style=\"color: #008000;\">\"src\/**\/*.{js,ts,tsx}\"<\/span>\r\n<span style=\"color: #008000;\">\/\/ include other folders if needed (e.g. \"lib\/**\", \"components\/**\")<\/span>\r\n<span style=\"color: #008000;\">],<\/span>\r\n<span style=\"color: #008000;\">\"ignore\": [<\/span>\r\n<span style=\"color: #008000;\">\"**\/__tests__\/**\", \/\/ test directories<\/span>\r\n<span style=\"color: #008000;\">\"android\/**\", \/\/ native Android project files<\/span>\r\n<span style=\"color: #008000;\">\"ios\/**\" \/\/ native iOS project files<\/span>\r\n<span style=\"color: #008000;\">\/\/ optionally add: \"coverage\/**\", \"e2e\/**\"<\/span>\r\n<span style=\"color: #008000;\">],<\/span>\r\n<span style=\"color: #008000;\">\"ignoreDependencies\": [<\/span>\r\n<span style=\"color: #008000;\">\"@react-native\/metro-config\", \/\/ Metro bundler config<\/span>\r\n<span style=\"color: #008000;\">\"@react-native\/babel-preset\" \/\/ Babel preset for RN<\/span>\r\n<span style=\"color: #008000;\">\/\/ add more tooling deps if flagged (e.g. \"jest\", \"babel-*\")<\/span>\r\n<span style=\"color: #008000;\">]<\/span>\r\n<span style=\"color: #008000;\">}<\/span><\/pre>\n<p>In this setup:<\/p>\n<ul>\n<li>Specific entry files like <span style=\"color: #008000;\">src\/index.ts<\/span> and <span style=\"color: #008000;\">src\/App.tsx<\/span> are declared explicitly.<\/li>\n<li>The project field defines which source files Knip should scan.<\/li>\n<li>Test folders and platform-specific directories for Android and iOS are excluded from analysis.<\/li>\n<li>Certain dependencies are intentionally ignored because they\u2019re required for build tooling but aren\u2019t referenced directly at runtime.<\/li>\n<\/ul>\n<p>Below is example of <strong>React web App<\/strong>:<\/p>\n<pre><span style=\"color: #008000;\">{<\/span>\r\n<span style=\"color: #008000;\">\"$schema\": \"https:\/\/unpkg.com\/knip@5\/schema.json\",<\/span>\r\n\r\n<span style=\"color: #008000;\">\"entry\": [<\/span>\r\n<span style=\"color: #008000;\">\"index.html\", \/\/ main HTML entry<\/span>\r\n<span style=\"color: #008000;\">\"src\/main.tsx\", \/\/ Vite entry<\/span>\r\n<span style=\"color: #008000;\">\"src\/index.tsx\" \/\/ CRA\/Webpack entry (optional)<\/span>\r\n<span style=\"color: #008000;\">\/\/ add more entry points if needed (e.g. admin.tsx, dashboard.tsx)<\/span>\r\n<span style=\"color: #008000;\">],<\/span>\r\n\r\n<span style=\"color: #008000;\">\"project\": [<\/span>\r\n<span style=\"color: #008000;\">\"src\/**\/*.{js,jsx,ts,tsx}\"<\/span>\r\n<span style=\"color: #008000;\">\/\/ include other folders if needed (e.g. \"lib\/**\", \"components\/**\")<\/span>\r\n<span style=\"color: #008000;\">],<\/span>\r\n\r\n<span style=\"color: #008000;\">\"ignore\": [<\/span>\r\n<span style=\"color: #008000;\">\"**\/*.test.*\", \/\/ unit tests<\/span>\r\n<span style=\"color: #008000;\">\"**\/__tests__\/**\", \/\/ test folders<\/span>\r\n<span style=\"color: #008000;\">\"dist\/**\", \/\/ production build (Vite)<\/span>\r\n<span style=\"color: #008000;\">\"build\/**\" \/\/ production build (CRA\/Webpack)<\/span>\r\n<span style=\"color: #008000;\">\/\/ add more like \"coverage\/**\", \".next\/**\"<\/span>\r\n<span style=\"color: #008000;\">],<\/span>\r\n\r\n<span style=\"color: #008000;\">\"ignoreDependencies\": [<\/span>\r\n<span style=\"color: #008000;\">\"@types\/*\", \/\/ TypeScript types<\/span>\r\n<span style=\"color: #008000;\">\"eslint\", \/\/ linting<\/span>\r\n<span style=\"color: #008000;\">\"prettier\", \/\/ formatting<\/span>\r\n<span style=\"color: #008000;\">\"vite\", \/\/ bundler<\/span>\r\n<span style=\"color: #008000;\">\"webpack\" \/\/ bundler<\/span>\r\n<span style=\"color: #008000;\">\/\/ add more tooling if flagged (jest, vitest, babel, etc.)<\/span>\r\n<span style=\"color: #008000;\">]<\/span>\r\n<span style=\"color: #008000;\">}<\/span><\/pre>\n<ul>\n<li>Entry points such as <span style=\"color: #008000;\">index.html<\/span> and <span style=\"color: #008000;\">src\/main.tsx<\/span> (or <span style=\"color: #008000;\">src\/index.tsx<\/span>) are explicitly defined to represent the application bootstrap.<\/li>\n<li>The project field specifies all source files within src\/ that Knip should analyze for unused exports and dependencies.<\/li>\n<li>Test files, test directories, and build output folders like dist\/ and build\/ are excluded to avoid noise in analysis.<\/li>\n<li>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.<\/li>\n<\/ul>\n<h2><\/h2>\n<h2>Conclusion<\/h2>\n<p>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 \u201cdead\u201d code slowly becomes technical debt. It can increase build times, make bundles larger, and make the codebase harder to understand.<\/p>\n<p>If you\u2019re 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.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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 [&hellip;]<\/p>\n","protected":false},"author":1271,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"iawp_total_views":27},"categories":[5881],"tags":[55,4064,5853],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/78930"}],"collection":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/users\/1271"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/comments?post=78930"}],"version-history":[{"count":3,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/78930\/revisions"}],"predecessor-version":[{"id":79156,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/posts\/78930\/revisions\/79156"}],"wp:attachment":[{"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/media?parent=78930"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/categories?post=78930"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tothenew.com\/blog\/wp-json\/wp\/v2\/tags?post=78930"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}