When Vite ignores your Browserslist configuration
Tags: vite, esbuild, browserslist, await
Published: 2022-12-26 11:18:48

---
title: When Vite ignores your Browserslist configuration
published: true
description: You think they work with each other, but they don’t. Let’s fix that!
tags: vite, esbuild, browserslist, await
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2022-12-26 11:19 +0000
---

I’m a long time user of [Browserslist](https://browsersl.ist/) and [Vite](https://vitejs.dev/) and I was pretty convinced they paired very-well. It seems to be the case for both CSS and JavaScript output, but it actually is not for JavaScript.

## The issue

Weirdly, it took me ages to notice the JavaScript transformations done by Vite (using [ESBuild](https://esbuild.github.io/) under the hood) always produce the same result, no matter your Browserslist configuration. Whoops!

In reality, the [issue](https://github.com/evanw/esbuild/issues/121) is that ESBuild doesn’t support Browserslist and uses its own [fine-grained `target` system](https://esbuild.github.io/api/#target) with a different syntax.

Knowing this, we are left with two options to honor our browsers target: maintaining a second list with a compliant ESBuild syntax (passed through Vite [`build.target`](https://vitejs.dev/config/build-options.html#build-target)), or use the following fix.

## The fix in two steps

Fortunately, there’s the [`browserslist-to-esbuild`](https://github.com/marcofugaro/browserslist-to-esbuild) package (by [Marco Fugaro](https://marcofuga.ro/)) that is able to properly pass the content of a `browserslist` file to ESBuild.

The steps described here can also be visualized [in a commit of mine](https://github.com/Sunappu-Dojo/burokku/pull/47/commits/b2ff0fe3e23313120644bce57cec4240af4828b6).

1. First, add `browserslist-to-esbuild` to your project (tested version: 1.2.0):

```sh
npm i -D browserslist-to-esbuild
```

2. Then, in your `vite.config.js` file:

```js
import { defineConfig } from 'vite'
import browserslistToEsbuild from 'browserslist-to-esbuild'

export default defineConfig({
  build: {
    target: browserslistToEsbuild(),
  },
})
```

That’s it! But wait, there’s more…

(Note: [another package](https://github.com/nihalgonsalves/esbuild-plugin-browserslist) can do this, but I have not tested it.)

## Unsupported JavaScript features

Now that ESBuild properly receives a `browserslist` config, maybe it will throw an error on compilation if you are trying to use a JavaScript feature unsupported by some of the browsers you target. In my case, I was trying to use top-level `await`, which is [available starting Safari 15](https://caniuse.com/mdn-javascript_operators_await_top_level) while I want to support Safari starting 12.2 (iOS) or 13.1 (macOS).

```sh
 ERROR  [vite:esbuild-transpile] Transform failed with 1 error:                                                                          18:07:46
js/helpers/TopLevelAwait.js:1:0: ERROR: Top-level await is not available in the configured target environment ("chrome100", "edge102", "firefox91", "ios12.2", "safari13.1" + 2 overrides)
```

I was well aware that some of my target browsers don’t support this feature: I’m actually using top-level `await` to detect another browser feature, so what I want is top-level `await` to remain in my code and crash in some browsers. This way, my app knows which workaround to use when the feature is not detected.

Well, we are lucky again, because ESBuild allows to receive a [list of “supported” features](https://esbuild.github.io/api/#supported) that will be prioritized over the compilation target coming from `browserslist`. All we have to do in our Vite configuration is to use the [`esbuild`](https://vitejs.dev/config/shared-options.html#esbuild) config entry to set `supported['top-level-await']` to `true`:

```js
export default defineConfig({
  esbuild: {
    /**
     * Prevents ESBuild to throw when using a feature not supported by the
     * list of supported browsers coming from the `browserslist` file.
     */
    supported: {
      'top-level-await': true,
    },
  },
}
```

Wit this, we can compile without error, and the JavaScript now preserves `await` at top-level.

With this knowledge in mind, I think it’s safe to say that Vite is only one step away to claim full Browserslist support, and that step is probably that Vite could use `browserslist-to-esbuild` to make everyone’s life easier.

## More on Vite and Browserslist

Actually, Vite doesn’t support Browserslist at all: the only reason it “seems” to work well with CSS is because Vite uses [PostCSS](https://postcss.org/), which itself natively uses Browserslist. We could say that Vite supports Browserslist by proxy, for CSS. I’m not the only one [who have been confused](https://github.com/vitejs/vite/discussions/6849) by this situation.

On the other hand, if your project uses the [`@vitejs/plugin-legacy`](https://github.com/vitejs/vite/tree/main/packages/plugin-legacy), an official Vite plugin helping you supporting browsers outside of [Vite browser compatibility list](https://vitejs.dev/guide/build.html#browser-compatibility), then Vite will make use of your Browserslist configuration because this plugin uses Babel [`@babel/preset-env`](https://babeljs.io/docs/en/babel-preset-env) instead of ESBuild, and this Babel plugin uses your Browserslist configuration out of the box in order to define the list of JavaScript transformations that Babel will apply.

As `@vitejs/plugin-legacy` has explicit [browser target documentation](https://github.com/vitejs/vite/tree/main/packages/plugin-legacy#targets) and even a [`ignorebrowserslistconfig` option](https://github.com/vitejs/vite/tree/main/packages/plugin-legacy#ignorebrowserslistconfig), I’m gonna end by quoting myself with a sentence from the previous section:

> Vite is only one step away to claim full Browserslist support, and that step is probably that Vite could use of `browserslist-to-esbuild` to make everyone’s life easier.

~~Or, for now, two steps away: [as mentioned](https://dev.to/kingyue/comment/23l3g) by @kingyue, there’s currently a [bug in `@vitejs/plugin-legacy`](https://github.com/vitejs/vite/issues/2476) (and a [fix in review](https://github.com/vitejs/vite/pull/11318) !), causing the Browserslist configuration to be ignored.~~ (Fixed in `@vitejs/plugin-legacy` 4.0.1.)