Unused imports are one of the most common forms of dead code in JavaScript and TypeScript projects. They accumulate silently as you refactor, move functions between files, and delete features. Each unused import adds noise to your code and can increase your production bundle size.
This guide covers three approaches to finding and removing them: ESLint rules, ts-prune, and automated scanning.
Why Unused Imports Matter
Every import statement tells your bundler to include that module in the dependency graph. While modern bundlers like Webpack, Rollup, and esbuild support tree-shaking, it does not always work:
- Barrel files (
index.tsthat re-exports everything) often defeat tree-shaking because the bundler cannot determine which re-exports have side effects - CommonJS imports (
require()) are not statically analyzable, so bundlers include the entire module - Side-effect imports (
import "./polyfill") must always be included, even if nothing is imported by name
The result: unused imports that you think are harmless may actually ship to production.
Method 1: ESLint Rules
The fastest way to catch unused imports is with ESLint. Two rules handle this:
no-unused-vars
TypeScript ESLint's @typescript-eslint/no-unused-vars catches imported values that are never referenced:
{
"rules": {
"@typescript-eslint/no-unused-vars": [
"error",
{
"argsIgnorePattern": "^_",
"varsIgnorePattern": "^_"
}
]
}
}
This flags any import that is not used in the file. The _ prefix pattern lets you intentionally mark variables as unused (common for destructured parameters you do not need).
Auto-fix with ESLint
Run ESLint with the --fix flag to automatically remove unused imports:
npx eslint --fix "src/**/*.{ts,tsx}"
Limitation: ESLint works per-file. It cannot tell you if an exported function is unused across your entire project -- only if an import is unused within the file that imports it.
Method 2: ts-prune for Project-Wide Detection
ts-prune analyzes your entire TypeScript project and reports exports that are never imported anywhere:
npx ts-prune
Output looks like:
src/utils/formatCurrency.ts:3 - formatCurrency
src/helpers/legacy.ts:1 - calculateDiscount
src/components/OldButton.tsx:5 - OldButton
Each line shows a file, line number, and the unused export name. If every export in a file is unused, the entire file is dead code.
Limitation: ts-prune only detects unused exports. It does not detect unused imports within a file (use ESLint for that) or unused local variables.
Method 3: Automated Scanning with CleanAI
CleanAI combines ESLint, ts-prune, and other language-specific tools into a single scan. It runs directly in VS Code or Cursor and presents findings in a visual panel:
- Open your project in VS Code or Cursor
- Run the "CleanAI: Analyze Codebase" command
- Review findings grouped by file
- Click "Remove" to delete individual findings, or use Auto Clean (Safe) to remove them incrementally with build verification
CleanAI handles the full pipeline: detecting unused imports, unused exports, unused functions, and orphaned files across TypeScript, JavaScript, Swift, and Python projects.
Safe Removal Workflow
Before bulk-removing unused imports, follow this process:
- Commit your current work so you have a clean rollback point
- Run the detection tool (ESLint, ts-prune, or CleanAI)
- Review dynamic imports -- some imports are used via
require()or string-based dynamic imports that static analysis cannot detect - Remove in small batches rather than all at once
- Run your test suite and build after each batch to catch regressions
Preventing Unused Imports
Add these to your development workflow to prevent unused imports from accumulating:
- Enable
no-unused-varsas an error (not a warning) in your ESLint config - Add ESLint to your CI pipeline so unused imports block merges
- Use your editor's auto-import cleanup -- VS Code can organize imports on save with
"editor.codeActionsOnSave": { "source.organizeImports": true } - Run periodic project-wide scans to catch unused exports that per-file linting misses
Unused imports are a small problem individually, but they compound across hundreds of files. Catching them early keeps your codebase clean and your bundles lean.