Knip Dead Code Detection
Knip is a comprehensive tool for finding unused code, dependencies, and exports in JavaScript and TypeScript projects. It helps maintain clean codebases and catch dead code before it accumulates.
Core Expertise
What is Knip?
- Unused detection: Files, dependencies, exports, types, enum members
- Plugin system: Supports 80+ frameworks and tools
- Fast: Analyzes large codebases in seconds
- Actionable: Clear reports with file locations
- CI-ready: Exit codes for failing builds
Key Capabilities
- Detect unused dependencies (npm, workspace packages)
- Find unused exports and types
- Identify unused files
- Discover unused enum members and class members
- Detect re-exports that aren't used
- Plugin support for frameworks (React, Next.js, Vue, Svelte)
- Integration with monorepos and workspaces
Installation
Examples below use
npx. Equivalent commands work withbun/bunxoryarn/yarn dlx.
# Project-local (recommended)
npm install --save-dev knip
# Verify installation
npx knip --version
Basic Usage
# Run Knip (scans entire project)
npx knip
# Show only unused dependencies
npx knip --dependencies
# Show only unused exports
npx knip --exports
# Show only unused files
npx knip --files
# Production mode (only check production dependencies)
npx knip --production
# Exclude specific issue types
npx knip --exclude-exports-used-in-file
# Output JSON (for CI)
npx knip --reporter json
# Debug mode (show configuration)
npx knip --debug
Configuration
Auto-detection (Zero Config)
Knip automatically detects:
- Entry points (package.json
main,exports,bin) - Frameworks (Next.js, Vite, Remix, etc.)
- Test runners (Vitest, Jest, Playwright)
- Build tools (ESLint, TypeScript, PostCSS)
No configuration needed for standard projects.
knip.json (Explicit Configuration)
{
"$schema": "https://unpkg.com/knip@latest/schema.json",
"entry": ["src/index.ts", "src/cli.ts"],
"project": ["src/**/*.ts"],
"ignore": ["**/*.test.ts", "scripts/**"],
"ignoreDependencies": ["@types/*"],
"ignoreBinaries": ["npm-check-updates"]
}
knip.ts (TypeScript Configuration)
// knip.ts
import type { KnipConfig } from 'knip';
const config: KnipConfig = {
entry: ['src/index.ts', 'src/cli.ts'],
project: ['src/**/*.ts', 'scripts/**/*.ts'],
ignore: ['**/*.test.ts', '**/*.spec.ts', 'tmp/**'],
ignoreDependencies: [
'@types/*', // Type definitions
'typescript', // Always needed
],
ignoreExportsUsedInFile: true,
ignoreWorkspaces: ['packages/legacy/**'],
};
export default config;
Recommended Production Setup
// knip.ts
import type { KnipConfig } from 'knip';
const config: KnipConfig = {
// Entry points
entry: [
'src/index.ts',
'src/cli.ts',
'scripts/**/*.ts', // Include scripts
],
// Project files
project: ['src/**/*.{ts,tsx}', 'scripts/**/*.ts'],
// Ignore patterns
ignore: [
'**/*.test.ts',
'**/*.spec.ts',
'**/__tests__/**',
'**/__mocks__/**',
'dist/**',
'build/**',
'coverage/**',
'.next/**',
],
// Dependencies to ignore
ignoreDependencies: [
'@types/*', // Type definitions used implicitly
'typescript', // Always needed for TS projects
'tslib', // TypeScript helper library
'@biomejs/biome', // Used via CLI
'prettier', // Used via CLI
],
// Binaries to ignore (used in package.json scripts)
ignoreBinaries: ['npm-check-updates', 'semantic-release'],
// Ignore exports used in the same file
ignoreExportsUsedInFile: true,
// Workspace configuration (for monorepos)
workspaces: {
'.': {
entry: ['src/index.ts'],
},
'packages/*': {
entry: ['src/index.ts', 'src/cli.ts'],
},
},
};
export default config;
Plugin System
Knip automatically detects and configures plugins for popular tools:
Framework Plugins
| Framework | Auto-detected | Entry Points |
|---|---|---|
| Next.js | next.config.js |
pages/, app/, middleware.ts |
| Vite | vite.config.ts |
index.html, config plugins |
| Remix | remix.config.js |
app/root.tsx, app/entry.* |
| Astro | astro.config.mjs |
src/pages/, config integrations |
| SvelteKit | svelte.config.js |
src/routes/, src/app.html |
| Nuxt | nuxt.config.ts |
app.vue, pages/, layouts/ |
Test Runner Plugins
| Tool | Auto-detected | Entry Points |
|---|---|---|
| Vitest | vitest.config.ts |
**/*.test.ts, config files |
| Jest | jest.config.js |
**/*.test.js, setup files |
| Playwright | playwright.config.ts |
tests/**/*.spec.ts |
| Cypress | cypress.config.ts |
cypress/e2e/**/*.cy.ts |
Build Tool Plugins
| Tool | Auto-detected | Entry Points |
|---|---|---|
| TypeScript | tsconfig.json |
Files in include |
| ESLint | .eslintrc.js |
Config files, plugins |
| PostCSS | postcss.config.js |
Config plugins |
| Tailwind | tailwind.config.js |
Config plugins, content files |
Plugin Configuration Override
// knip.ts
const config: KnipConfig = {
// Disable specific plugins
eslint: false,
prettier: false,
// Override plugin config
vitest: {
entry: ['vitest.config.ts', 'test/setup.ts'],
config: ['vitest.config.ts'],
},
next: {
entry: [
'next.config.js',
'pages/**/*.tsx',
'app/**/*.tsx',
'middleware.ts',
'instrumentation.ts',
],
},
};
Ignoring Issues
Ignore Patterns
// knip.ts
const config: KnipConfig = {
// Ignore entire directories
ignore: ['legacy/**', 'vendor/**'],
// Ignore specific dependencies
ignoreDependencies: [
'@types/*',
'some-peer-dependency',
],
// Ignore specific exports
ignoreExportsUsedInFile: {
interface: true, // Ignore interfaces used only in same file
type: true, // Ignore types used only in same file
},
// Ignore workspace packages
ignoreWorkspaces: ['packages/deprecated/**'],
};
Inline Comments
// Ignore unused export
// @knip-ignore-export
export const unusedFunction = () => {};
// Ignore unused dependency in package.json
{
"dependencies": {
"some-package": "1.0.0" // @knip-ignore-dependency
}
}
Whitelist Pattern
// knip.ts - Whitelist specific exports
const config: KnipConfig = {
entry: ['src/index.ts'],
project: ['src/**/*.ts'],
// Only these exports are allowed to be unused (public API)
exports: {
include: ['src/index.ts'],
},
};
CI/CD Integration
GitHub Actions
name: Knip
on:
push:
branches: [main]
pull_request:
jobs:
knip:
name: Check for unused code
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 'lts/*'
- name: Install dependencies
run: npm ci
- name: Run Knip
run: npx knip --production
- name: Run Knip (strict)
run: npx knip --max-issues 0
GitLab CI
knip:
image: node:lts
stage: test
script:
- npm ci
- npx knip --production
only:
- merge_requests
- main
Pre-commit Hook
# .pre-commit-config.yaml
repos:
- repo: local
hooks:
- id: knip
name: Knip
entry: npx knip
language: system
pass_filenames: false
Common Patterns
Check Only Unused Dependencies
# Fastest check - only dependencies
npx knip --dependencies
# Exit with error if any unused dependencies
npx knip --dependencies --max-issues 0
Use in CI to enforce strict dependency hygiene.
Check Only Exports (Library Development)
# Check for unused exports
npx knip --exports
# Allow exports used in same file
npx knip --exports --exclude-exports-used-in-file
Use for libraries to ensure clean public API.
Production vs Development Dependencies
# Check production code only
npx knip --production
# Check everything (including dev dependencies)
npx knip
Monorepo Workflows
// knip.ts
const config: KnipConfig = {
workspaces: {
'.': {
entry: ['scripts/**/*.ts'],
ignoreDependencies: ['@org/internal-package'],
},
'packages/web': {
entry: ['src/index.ts', 'src/App.tsx'],
ignoreDependencies: ['react', 'react-dom'], // Provided by parent
},
'packages/api': {
entry: ['src/server.ts'],
},
},
};
# Check all workspaces
npx knip
# Check specific workspace
npx knip --workspace packages/web
Interpreting Results
Example Output
✓ No unused files
✓ No unused dependencies
✗ 2 unused exports
src/utils.ts:
- calculateTax (line 42)
- formatDate (line 58)
src/types.ts:
- UserRole (line 12)
Issue Types
| Type | Description | Action |
|---|---|---|
| Unused file | File not imported anywhere | Delete or add to entry points |
| Unused dependency | Package in package.json not used | Remove from dependencies |
| Unused export | Exported but never imported | Remove export or make private |
| Unused type | Type/interface exported but unused | Remove or make internal |
| Unused enum member | Enum member never referenced | Remove member |
| Duplicate export | Same export from multiple files | Consolidate exports |
Troubleshooting
False Positives (Exports Used via Side Effects)
// knip.ts
const config: KnipConfig = {
// Ignore exports that are used via side effects
ignoreExportsUsedInFile: true,
// Or add to entry points
entry: ['src/index.ts', 'src/side-effect-file.ts'],
};
Knip Not Finding Entry Points
# Debug configuration
npx knip --debug
# Manually specify entry points
npx knip --entry src/index.ts --entry src/cli.ts
Performance Issues
# Exclude node_modules explicitly (usually automatic)
npx knip --exclude '**/node_modules/**'
# Use .gitignore patterns
npx knip --include-libs false
# Increase memory limit
NODE_OPTIONS=--max-old-space-size=4096 npx knip
Plugin Not Detected
// knip.ts - Force enable plugin
const config: KnipConfig = {
vite: {
entry: ['vite.config.ts'],
config: ['vite.config.ts'],
},
};
Unused Dependencies in Scripts
// package.json - Knip detects binaries in scripts
{
"scripts": {
"lint": "eslint .", // Detects eslint dependency
"test": "vitest" // Detects vitest dependency
}
}
If not detected:
// knip.ts
const config: KnipConfig = {
ignoreDependencies: ['eslint', 'vitest'],
};
Advanced Usage
Custom Reporters
# JSON output (for CI)
npx knip --reporter json > knip-report.json
# Compact output
npx knip --reporter compact
# Custom format (coming soon)
npx knip --reporter custom
Incremental Checks (Changed Files Only)
# Check only changed files (requires git)
npx knip --changed
# Since specific commit
npx knip --changed --base main
Type Checking Integration
// knip.ts
const config: KnipConfig = {
// Include type-only imports as used
includeTypeImports: true,
// Check for unused TypeScript types
types: true,
};
Best Practices
Start with Dependencies Only
# Easiest wins first
npx knip --dependencies
# Then move to exports
npx knip --exports
# Finally check files
npx knip --files
Gradual Adoption
// knip.ts - Start strict, then relax
const config: KnipConfig = {
// Start with critical paths only
entry: ['src/index.ts'],
project: ['src/core/**/*.ts'],
// Expand coverage over time
// entry: ['src/**/*.ts'],
// project: ['src/**/*.ts'],
};
CI Strategy
# Check dependencies in CI (fast, high value)
- name: Check unused dependencies
run: npx knip --dependencies --max-issues 0
# Check exports in PR (prevents API bloat)
- name: Check unused exports
run: npx knip --exports
if: github.event_name == 'pull_request'
Maintenance Schedule
- Weekly: Run full Knip scan, clean up issues
- PR Review: Check for new unused exports
- Pre-release: Full scan with
--production - Refactors: Run Knip before and after
References
- Official docs: https://knip.dev
- Configuration: https://knip.dev/reference/configuration
- Plugins: https://knip.dev/reference/plugins
- CLI reference: https://knip.dev/reference/cli
- FAQ: https://knip.dev/reference/faq