Browserslist: one target-browser config shared by Autoprefixer, Babel and Stylelint
🦔 Share target browsers between different front-end tools, like Autoprefixer, Stylelint and babel-preset-env
At a glance
- What is it?
- Browserslist is the config layer that tells several front-end tools which browsers and Node.js versions to target. It is small, MIT-licensed and boring on purpose, and the interesting parts are the query language, the shared config files, and what happens when caniuse-lite goes stale.
- Who is it for?
- Adopt Browserslist if you run more than one front-end tool that needs a browser target, because the alternative is maintaining the same list in three places. Skip it if your build has a single consumer with its own target syntax and you never need to reconcile them.
- 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 received new commits within the last day.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The duplicated browser list problem Browserslist removes
Autoprefixer needs to know which browser prefixes to emit. Babel needs to know which syntax to transpile. Stylelint and eslint-plugin-compat need the same list to flag unsupported features. Without a shared source, each tool gets its own list, and they drift. A team adds Safari 15 to the Autoprefixer config, forgets the Babel config, and ships a bundle that is transpiled for a different set of browsers than the CSS is prefixed for.
Browserslist is the shared source. It is a config format plus a resolver: tools that integrate it read one definition and ask Browserslist to expand it into a concrete list of browser versions. The README lists Autoprefixer, Babel, postcss-preset-env, eslint-plugin-compat, stylelint-no-unsupported-browser-features, postcss-normalize and obsolete-webpack-plugin among the consumers. The target audience is front-end build maintainers, not application developers: the people who own the toolchain and get the bug report when a browser renders wrong.
How a query becomes a concrete browser list
The config holds queries, not versions. `last 2 versions` and `maintained node versions` are strings that Browserslist resolves at build time against data from caniuse-lite, which wraps Can I Use data. The package.json dependencies also list baseline-browser-mapping, electron-to-chromium, node-releases and update-browserslist-db, so browser versions, Electron-to-Chromium mappings and Node.js release data each come from their own dataset rather than being hardcoded in the resolver.
Resolution order matters. The README states that Browserslist takes queries from `.browserslistrc` in the current or parent directories first, then the `browserslist` key in `package.json` in the current or parent directory. That means a stray `.browserslistrc` higher up the tree can override what you put in package.json, which is a common source of confusion in monorepos.
The repository layout reflects the split: `index.js` for the main resolver, `node.js` for Node.js target resolution, `browser.js` as the browser-field replacement for `node.js`, `parse.js` for the query grammar, and `cli.js` for the command-line tool. There is a `grammar.w3c-ebnf` file, and the package.json `browser` field maps `./node.js` to `./browser.js` and disables `path`, so the resolver can be bundled for browser contexts without pulling in Node built-ins.
Installing Browserslist and checking your first config
Browserslist is published on npm and declares a `browserslist` binary in package.json pointing at `cli.js`. In a project that already uses Autoprefixer, the README says the CLI is built in, so you can inspect the resolved list without installing anything extra:
npx browserslistRun it from the project directory. It prints the concrete browser versions your config resolves to, which is the fastest way to see whether a query does what you assumed.
The README gives two ways to declare the targets. In package.json:
"browserslist": [
"defaults and fully supports es6-module",
"maintained node versions"
]Or in a `.browserslistrc` file, which the README shows with a comment line:
# Browsers that we support
defaults and fully supports es6-module
maintained node versionsEvery integrated tool reads whichever of the two it finds first, so you only write the list once. If you only want the reasonable starting point, the README documents a `defaults` query and shows it as a single-element array in package.json. After editing, re-run `npx browserslist` and confirm the printed versions moved in the direction you expected.
The defaults query hides more than it reveals
`defaults` is convenient and the README calls it a reasonable configuration for most users. It is also opaque: the query expands to a set of rules that change as caniuse-lite data changes, so the browsers you support can shift between installs without any edit to your config. That is the design working as intended, and it is still a real operational surprise when a build starts emitting different output after a dependency bump.
The README is explicit about the opposite failure too. It recommends `last 2 versions, not dead, > 0.2%` if you want to change the default set, and warns that `last n versions` alone does not add popular old versions, while a percentage-only rule can entrench popular browsers and push toward the stagnation the README compares to Internet Explorer 6. It also argues against selecting browsers directly such as `last 2 Chrome versions` unless you are building for a kiosk with one browser, and points out that Opera Mini has roughly 100 million users in Africa and that QQ Browser has more market share than Firefox and desktop Safari combined. Those are the README's own numbers and its own framing, and they are the reason a narrow config is a product decision, not a build detail.
Stale caniuse-lite and the update-browserslist-db CLI
Queries like `last 2 versions` and `>1%` are only as current as the data behind them. The README documents `update-browserslist-db`, a CLI tool that updates the browsers database used by those queries. The package.json lists `update-browserslist-db` as a dependency, and the README also links `browserslist-update-action`, a GitHub Action that runs the update and proposes a pull request.
This is where the tool can quietly mislead. If the database is months old, `last 2 versions` resolves to versions that are no longer the last two, and the CLI will print them with full confidence. Nothing in the resolver errors; the output looks fine. The README's own tooling section treats database updating as a separate concern from query resolution, which is honest about the boundary and also means the update step is yours to schedule.
A second limitation is scope. Browserslist resolves targets; it does not verify that your code actually works in them. eslint-plugin-compat and stylelint-no-unsupported-browser-features can flag features, but the resolver itself only produces a list. If you need runtime detection, the README points at separate packages such as browserslist-useragent-regexp, which compiles a query into a RegExp for testing a user agent string, and browserslist-useragent-ruby for the same job in Ruby. Those are not part of the core package.
Custom usage data and shareable configs
Percentage queries can be driven by your own traffic instead of global Can I Use numbers. The README documents a `>5% in my stats` style query and lists several tools for producing the data file: browserslist-plausible pulls statistics from Plausible, browserslist-ga and browserslist-ga-export pull from Google Analytics, and browserslist-new-relic generates a custom usage data file. The README table of contents also has a Custom Usage Data section.
The trade-off is that your analytics now sit in the build path. A stats file that is stale, sampled from a logged-in audience, or skewed by bots changes which browsers you transpile for, and the change is invisible in the diff because the file is data, not code. Global Can I Use numbers are less precise but they do not depend on your analytics pipeline being correct.
Shareable configs are the other extension point. The README has a Shareable Configs section, which is the mechanism for publishing a query set as a package and extending it, useful when several repositories in an organization should target the same browsers. The README does not spell out the resolution rules between a shareable config and a local override in the excerpt available, so treat the interaction as something to verify with `npx browserslist` in each repository rather than assume.
When Browserslist is the wrong layer
Browserslist is a coordination tool. If you have exactly one consumer, say a single bundler with its own target syntax, adding Browserslist inserts a resolution step and a data dependency for no coordination benefit. The same applies when your target is not a browser list at all: a Node.js-only service that pins one runtime version gains nothing from percentage queries.
There is also a versioning cost. The package depends on caniuse-lite, baseline-browser-mapping, electron-to-chromium, node-releases and update-browserslist-db, so every one of those datasets can move your resolved list. In a lockfile-driven workflow that is manageable; in a floating-dependency workflow it is a source of non-reproducible builds. The README does not document a pinning strategy for the data packages in the excerpt available, so if reproducibility matters, the lockfile is where you enforce it.
A concrete alternative for teams that want the same idea without the npm data packages is browserslist-rs, listed in the README as a Browserslist port to Rust. The difference in approach is the runtime and the packaging, not the query language: it targets Rust and non-Node toolchains, which matters if your build pipeline is not JavaScript. If your pipeline is JavaScript, the port adds a cross-language boundary for no benefit.
Editorial conclusion
Adopt Browserslist if you run more than one front-end tool that needs a browser target, because the alternative is maintaining the same list in three places. Skip it if your build has a single consumer with its own target syntax and you never need to reconcile them. Before rolling it out, run npx browserslist in the project directory and read the printed list, then check the browserslist key in package.json against the .browserslistrc file if both exist, since the resolution order decides which one wins.
Frequently asked questions
How do I use Browserslist in my project?
Add a `browserslist` key to package.json or create a `.browserslistrc` file, then let the integrated tools read it. The README shows `npx browserslist` as the way to print the resolved target browsers from the project directory.
How do I add Browserslist to package.json?
Add a `browserslist` array containing query strings. The README's example uses `"defaults and fully supports es6-module"` and `"maintained node versions"` as the two entries.
What is Browserslist in package.json?
It is the key Browserslist reads to find the target browser and Node.js queries for the project. The README states queries are taken from `.browserslistrc` in the current or parent directories first, then from the `browserslist` key in package.json.
What is Browserslist used for?
It shares target browsers and Node.js versions between front-end tools so each one does not keep its own list. The README names Autoprefixer, Babel, postcss-preset-env, eslint-plugin-compat, stylelint-no-unsupported-browser-features, postcss-normalize and obsolete-webpack-plugin as consumers.
What is update-browserslist-db?
It is a CLI tool that updates the browsers database used by queries such as `last 2 version` or `>1%`. The README lists it in the tools section, and the package.json includes it as a dependency.
Official sources
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.
[](https://hysenlabs.com/projects/browserslist-browserslist)