Library / SDK
amilajack/eslint-plugin-compat avatar
amilajack/eslint-plugin-compat

eslint-plugin-compat: browser compatibility linting for Web APIs

Check the browser compatibility of your code

3,185 stars115 forksTypeScriptMIT

At a glance

What is it?
eslint-plugin-compat reports Web APIs and ES APIs that your browserslist targets do not support, at lint time instead of in production. It fits teams that already run ESLint and already maintain a browserslist config.
Who is it for?
Adopt eslint-plugin-compat if you already run ESLint 9 flat config and browserslist, because the plugin reuses that target list instead of asking you to maintain a second one. Skip it if you only ship to a single evergreen browser, or if you expect it to catch runtime failures: it reads static API references and knows nothing about your actual traffic.
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?
Yes. The repository last received commits 78 days 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 4, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What eslint-plugin-compat checks, and who ends up reading its output

The plugin lints the browser compatibility of the APIs your code calls. That is a narrower job than it sounds. It does not check syntax, does not check CSS, and does not tell you whether a feature works correctly where it exists. It answers one question: is the API you just referenced available in the browsers your project targets?

The audience is teams that ship a web bundle to more than one browser family and already have a browserslist query somewhere in the repository. The README frames the motivation bluntly: native toolchains for iOS and Android have had API linting from the start, and the author wanted the same for the web so developers would not have to memorize Web API compatibility. If your build already runs browserslist to pick Babel or PostCSS targets, this plugin consumes that same list. There is no second configuration file to keep in sync.

That reuse is the whole pitch. A team that has never written a browserslist query gets less from it, because the plugin will fall back to browserslist defaults and report against targets nobody chose deliberately.

How the plugin resolves an API reference against a browser target list

Three pieces of data meet inside the lint run. The first is the set of API references in your source, which the plugin collects by walking the AST. The second is your browserslist query, which browserslist resolves into concrete browser versions. The third is compatibility data, which the README points at through two related projects: ast-metadata-inferer and compat-db. The plugin's package keywords name caniuse and kangax, which is where that compatibility data originates.

The AST step matters for how precise the reports are. The README's polyfill examples distinguish four shapes of reference: a whole API such as Promise, a single method such as WebAssembly.compile, a bare function such as fetch, and an instance method such as Array.prototype.push, which the README says must be written with the .prototype. segment. Those four shapes are the granularity at which the plugin reasons. It is not matching strings in your source; it is recognising member expressions and identifiers.

Two settings change what gets reported. lintAllEsApis is described as experimental and disabled by default, so ES API coverage is opt-in rather than the baseline. ignoreConditionalChecks controls whether guarded code such as a feature detection block is reported; by default the README states that feature detection does not trigger a report, and setting the flag makes the plugin lint those conditionals anyway. That default is defensible. A guard is evidence the author already thought about absence. Flagging it produces noise on code that is correct.

Installing eslint-plugin-compat and getting a first warning

The README gives a three-step setup. Install the package from npm first.

bash
npm install eslint-plugin-compat

Then register the flat config preset. The README's example file is eslint.config.mjs, and it imports the plugin's default export and spreads the recommended flat config into the exported array.

js
// eslint.config.mjs
import compat from "eslint-plugin-compat";

export default [compat.configs["flat/recommended"]];

Targets come from browserslist, and the README shows the query living in package.json. When no configuration is found, browserslist falls back to "> 0.5%, last 2 versions, Firefox ESR, not dead".

jsonc
{
  // ...
  "browserslist": ["defaults"],
}

Run ESLint over your source and the plugin reports API references that fall outside those targets. The README links a minimal demo repository, amilajack/eslint-plugin-compat-demo, for a working example. If the first run produces nothing, that is a real result: your code may not touch any API outside its targets.

Declaring polyfills, and why the name you write matters

A compatibility warning is only useful if the API is genuinely absent. If a polyfill is already loaded, the report is a false positive, and the plugin's answer is a settings block listing what you have polyfilled. The README's examples cover the four reference shapes, and the comments in that block are the specification.

jsonc
{
  "settings": {
    "polyfills": [
      "Promise",
      "WebAssembly.compile",
      "fetch",
      "Array.prototype.push",
    ],
  },
}

Read the comments in the README's version of this block carefully, because the format is exact. A whole API and all its methods and properties is the bare name. A specific method is the object and property joined by a dot. A function with no property is just its name. An instance method needs the .prototype. segment. Getting this wrong is silent: a mistyped entry does not error, it simply fails to suppress the report it was meant to suppress, and you are back to chasing a warning you already resolved.

The list is also a maintenance obligation. Every polyfill you add to the bundle has to be added here too, or the warnings drift away from reality. Nothing in the README suggests the plugin reads your bundle to discover what is actually polyfilled.

Where eslint-plugin-compat gives you a false sense of safety

The plugin is static. It sees API references in your source and compares them to a target list. It cannot see what your users actually run, and it cannot see code paths that only execute under conditions your lint run never evaluates. A reference inside a dynamically constructed property access, an API reached through a string key, or a call assembled at runtime will not be recognised as that API.

The polyfill list is the second gap, and it is the sharper one. Declaring Promise in settings.polyfills tells the plugin that Promise is handled. It does not verify that the polyfill is loaded before the first call, that it is loaded in every entry point, or that it covers the methods your code uses. The plugin trusts the declaration. If your polyfill setup is conditional, the lint result is optimistic in exactly the environments you care about.

The third gap is scope. This is a browser compatibility linter. It says nothing about Node APIs, nothing about server-side rendering paths, and nothing about CSS. A team that reads a clean lint output as a compatibility guarantee has misread what the tool checks. The README describes the ES API support as experimental and off by default, so even within JavaScript the coverage is not the full language surface unless you turn that setting on and accept its experimental status.

eslint-plugin-compat compared with linting for syntax and platform rules

The nearest neighbours in the ESLint ecosystem solve different problems. eslint-plugin-es-x targets ECMAScript syntax and language feature usage, and its scope is the language rather than the host environment. eslint-plugin-compat targets the browser APIs your code calls and resolves them against a browserslist query. The overlap is real but the inputs differ: one reasons about language level, the other about which browsers you support.

That distinction decides which tool you need. If your concern is that a syntax form will not parse in an older engine, syntax linting is the relevant check, and it does not need a browserslist query at all. If your concern is that fetch or WebAssembly.compile is missing in a browser you still support, the compatibility data is the relevant check, and it needs the target list. Running both is reasonable because neither subsumes the other.

The plugin's own design choice is to depend on browserslist rather than maintain a target list of its own. That is a real advantage for a project already using browserslist, and a real cost for one that is not, since adopting the plugin then means adopting browserslist semantics as well.

Licence, release cadence and the cost of keeping targets current

The project is MIT licensed, stated in package.json and in the LICENSE file at the repository root. MIT is permissive: it allows use, modification and redistribution provided the copyright notice and permission notice are preserved. That is a statement about the licence text, not legal advice, and the compatibility data the plugin draws on comes from other sources whose terms are outside this repository.

Maintenance looks current. The last push to the default branch was on 2026-07-23, and the most recent release in the list is v7.0.2 on 2026-04-29, following v7.0.1 and v7.0.0 earlier in 2026. The repository is not archived. The package exposes both ESM and CommonJS entry points through the exports field, with main pointing at ./lib/cjs/src/index.js, so the plugin works from either module system.

The upgrade cost sits in the target list rather than in the plugin. Browser versions move, and a browserslist query like "last 2 versions" resolves differently over time without a line of your code changing. A warning that appears after a dependency bump may come from a new API reference in that dependency, or from a target that shifted underneath you. The README does not document a rollback path or a way to pin the compatibility data independently of the plugin version, so the practical approach is to pin the plugin version in your lockfile and read the CHANGELOG before moving it.

Editorial conclusion

Adopt eslint-plugin-compat if you already run ESLint 9 flat config and browserslist, because the plugin reuses that target list instead of asking you to maintain a second one. Skip it if you only ship to a single evergreen browser, or if you expect it to catch runtime failures: it reads static API references and knows nothing about your actual traffic. Verify first that your browserslist query is the one you mean, since the README notes the fallback is "> 0.5%, last 2 versions, Firefox ESR, not dead" when no configuration is found, and that settings.polyfills lists match the names the plugin checks rather than the names your bundler injects.

Frequently asked questions

How do I install eslint-plugin-compat?

Install it with npm install eslint-plugin-compat, then add the flat config preset to your eslint.config.mjs by exporting compat.configs["flat/recommended"]. Browser targets come from your browserslist configuration.

What are ESLint plugins?

ESLint plugins add rules and presets that the core linter does not ship. eslint-plugin-compat is one: it contributes compatibility rules and a recommended flat config preset that you spread into your exported config array.

Does eslint-plugin-compat work with TypeScript projects?

The repository's primary language is TypeScript and the package ships type declarations for both the ESM and CommonJS entry points, so the plugin is consumed from TypeScript projects through the standard ESLint config. The README's setup steps do not differ for TypeScript.

How does eslint-plugin-compat decide which browsers to check against?

It uses browserslist. You configure targets in package.json or another browserslist location, and if no configuration is found browserslist defaults to "> 0.5%, last 2 versions, Firefox ESR, not dead".

Why does eslint-plugin-compat still warn about an API I already polyfilled?

Because the polyfill has to be declared in the settings.polyfills list, and the entry must match the reference shape exactly: a bare name for a whole API, object and property joined by a dot for a method, and a .prototype. segment for an instance method. A mismatched entry does not suppress the report.

Does eslint-plugin-compat check ES APIs as well as Web APIs?

It can, but the README describes ES API linting as experimental and disabled by default. Setting settings.lintAllEsApis to true enables it.

Official sources

  1. amilajack/eslint-plugin-compat on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
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/amilajack-eslint-plugin-compat.svg)](https://hysenlabs.com/projects/amilajack-eslint-plugin-compat)