Library / SDK
sweepline/eslint-plugin-unused-imports avatar
sweepline/eslint-plugin-unused-imports

eslint-plugin-unused-imports: splitting unused variables from unused imports

Package to separate no-unused-vars and no-unused-imports for eslint as well as providing an autofixer for the latter.

627 stars25 forksTypeScriptMIT

At a glance

What is it?
eslint-plugin-unused-imports splits the built-in no-unused-vars rule into two, so unused imports can be reported and auto-fixed while unused local variables stay a warning you can tune. It is a small plugin with a narrow purpose, and the version matrix in its README is the part you will actually read.
Who is it for?
Adopt eslint-plugin-unused-imports if your team wants unused imports removed automatically while unused variables stay a warning, and you are on the ESLint and typescript-eslint versions that its README maps to your plugin major. Do not adopt it if you are on a typescript-eslint version outside the listed range, because the plugin composes the other plugin's rule rather than implementing its own.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Activity is slowing. The repository last received commits 6 months ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The plugin composes no-unused-vars instead of replacing it

eslint-plugin-unused-imports finds and removes unused ES6 module imports. The way it does that is the interesting part, and the README states it directly: the plugin works by splitting up the no-unused-vars rule depending on whether the reported node is an import statement in the AST, and it provides an autofix rule that removes the nodes when they are imports.

So this is not a from-scratch unused import detector. It composes the no-unused-vars rule from either the typescript plugin or the js plugin, and it partitions the results. The consequence is stated in the same sentence and it is a real constraint: the other plugin needs to be installed and reporting correctly for any of this to work.

That design is why the plugin exists. The built-in rule treats an unused import as just another unused variable, and the built-in rule has no autofix for it. Splitting the two lets an import be removed safely by the fixer while a local variable is reported with the severity and options you choose.

The package description says the same thing in a shorter form: report and remove unused ES6 modules. The supported rules are no-unused-imports and no-unused-vars.

The version table is the first thing to read

The README has a Versions section that maps plugin majors to the ESLint and typescript-eslint versions they work with, and it is worth reading before anything else.

Version 4.1.x is for ESLint 9 with typescript-eslint 5 to 8. Version 4.0.x is for ESLint 9 with typescript-eslint 8. Version 3.x.x is for ESLint 8 with typescript-eslint 6 to 7. Version 2.x.x is for ESLint 8 with typescript-eslint 5. Version 1.x.x covers ESLint 6 and 7.

The package manifest corroborates the current line. The peer dependencies allow eslint from 8 through 10 and typescript-eslint from 5 through 8, with the typescript-eslint peer marked optional, since a JavaScript-only project does not need it. The dev dependencies install aliased copies of typescript-eslint at versions 5, 6, 7 and 8, which is a reasonable way to test a plugin across the range it claims to support.

The most recent release is v4.4.1, tagged on 2026-02-09. The last push to the master branch was on 2026-04-08, so the project is not archived but recent work on the branch has not produced a new tagged release.

Installing it means matching the scope ESLint runs in

The installation is two npm commands. ESLint itself comes first, along with typescript-eslint if you are on TypeScript:

bash
npm i eslint --save-dev

Then the plugin:

bash
npm install eslint-plugin-unused-imports --save-dev

The scope detail is the one people trip over. The README notes that if you installed ESLint globally with the -g flag, you must also install eslint-plugin-unused-imports globally. ESLint resolves plugins relative to itself, so a plugin installed locally next to a globally installed ESLint is invisible to it, and the failure looks like the rule simply not existing rather than a resolution error.

For TypeScript projects, the README says to make sure you use both @typescript-eslint/eslint-plugin and @typescript-eslint/parser, since the plugin composes the typescript plugin's rule and needs it working.

The repository is a pnpm workspace, with pnpm-lock.yaml and pnpm-workspace.yaml at the root, and the manifest names pnpm 10.18.3 as the package manager. The build uses tsup, the tests use vitest, and formatting is prettier.

Turning off the built-in rule is the step that makes it work

The usage section shows a flat config file, eslint.config.js, and the shape of it matters more than it looks. The plugin is added to the plugins section under the name unused-imports, and then the built-in rule is turned off while the two plugin rules are turned on:

js
import unusedImports from "eslint-plugin-unused-imports";

export default [{
    plugins: {
        "unused-imports": unusedImports,
    },
    rules: {
        "no-unused-vars": "off", // or "@typescript-eslint/no-unused-vars": "off",
        "unused-imports/no-unused-imports": "error",
        "unused-imports/no-unused-vars": [
            "warn",
            {
                "vars": "all",
                "varsIgnorePattern": "^_",
                "args": "after-used",
                "argsIgnorePattern": "^_",
            },
        ]
    }
}];

Without the first line, you get duplicate reports, because the built-in rule and the plugin rule are both active. The comment in the example covers the TypeScript case, where the rule to disable is @typescript-eslint/no-unused-vars rather than the plain one.

The severity difference is deliberate and readable in the config: unused imports are an error, unused variables are a warning, and the variables rule is configured with the usual ignore patterns so that a name beginning with the ignored prefix stays exempt. That combination is the point of the plugin. An import that is dead is dead and can be removed by the fixer, while an unused variable is often a sign of a bug in progress, so it stays visible without failing a build.

React projects need two more rules or everything looks unused

There is a short section about React that saves a lot of confusion. If you write React code you need eslint-plugin-react, and you need to enable the two rules react/jsx-uses-react and react/jsx-uses-vars. Without them, all imports for components are reported as unused.

The reason is that JSX compiles to function calls, so the reference to a component in a template is invisible to a static analysis of identifiers. The two react rules exist to mark those identifiers as used, and they only do it if they are enabled. The unused-imports plugin composes the underlying no-unused-vars rule, so it inherits the blind spot exactly.

This is the classic failure mode of any import analysis in a JSX codebase: a wall of false positives that looks like the tool is broken. The fix is not to change the unused-imports configuration, it is to enable the two React rules that mark JSX usage.

The plugin is agnostic about frameworks beyond that. There is no React-specific code in the package, only the two rules the README tells you to switch on.

What the autofix can and cannot remove

The autofix is the reason to use this plugin instead of the built-in rule, and it is also the part with limits. The fixer removes the import nodes that the rule reports, which means it only touches statements whose only purpose is the import.

Two practical implications follow. First, the fix is safe to run automatically because it deletes something rather than rewriting it, so eslint --fix on a file with unused imports removes those lines and leaves everything else alone. Second, the plugin composes another plugin's rule, so the set of reported nodes is whatever that rule reports, and the fixer can only remove what the composed rule identified as an import in the AST.

That is also the reason the plugin is scoped narrowly. It handles the ES6 module import case. It does not attempt to detect unused files, unused exports, or dependencies in package.json that nothing imports, which are separate problems that need separate tools, and a project that expects one plugin to solve all three will be disappointed.

The repository keeps the implementation in src, the tests in a vitest setup at vitest.config.ts, and the build output in dist, with tsup.config.ts and tsconfig.json at the root. The published package ships only the dist directory, with separate CommonJS and ES module entry points declared in the exports field.

Editorial conclusion

Adopt eslint-plugin-unused-imports if your team wants unused imports removed automatically while unused variables stay a warning, and you are on the ESLint and typescript-eslint versions that its README maps to your plugin major. Do not adopt it if you are on a typescript-eslint version outside the listed range, because the plugin composes the other plugin's rule rather than implementing its own. Before you upgrade, check the peer dependency ranges in package.json, install the plugin in the same scope as ESLint itself, and enable the react/jsx-uses-react and react/jsx-uses-vars rules if you write React, or every component import will be reported as unused.

Frequently asked questions

What does eslint-plugin-unused-imports do?

It splits the no-unused-vars rule into two, so unused imports are reported and auto-fixed while unused variables are reported separately with their own severity and options. It composes the no-unused-vars rule from the typescript or js plugin rather than implementing its own detection.

Which ESLint versions does eslint-plugin-unused-imports support?

The README maps them out: 4.1.x for ESLint 9 with typescript-eslint 5 to 8, 4.0.x for ESLint 9 with typescript-eslint 8, 3.x.x for ESLint 8 with typescript-eslint 6 to 7, 2.x.x for ESLint 8 with typescript-eslint 5, and 1.x.x for ESLint 6 and 7. The package manifest allows ESLint 8 to 10 and typescript-eslint 5 to 8 as peer dependencies.

Why does eslint-plugin-unused-imports report all my React imports as unused?

You need eslint-plugin-react with the rules react/jsx-uses-react and react/jsx-uses-vars enabled. The README states that otherwise all imports for components will be reported as unused, because JSX usage is not visible to the underlying rule.

How do I configure eslint-plugin-unused-imports in a flat config?

Add unusedImports to the plugins section under the name unused-imports, turn the built-in no-unused-vars off, then enable unused-imports/no-unused-imports as an error and unused-imports/no-unused-vars as a warning with your usual ignore patterns. The README shows the full eslint.config.js example.

Official sources

  1. Issues
  2. License: MIT
  3. README
  4. Releases
  5. sweepline/eslint-plugin-unused-imports on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/sweepline-eslint-plugin-unused-imports.svg)](https://hysenlabs.com/projects/sweepline-eslint-plugin-unused-imports)

Was this useful?