DEV Community

Mehdi M.
Mehdi M.

Posted on • Edited on

When Vite ignores your Browserslist configuration

I’m a long time user of Browserslist and Vite 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 under the hood) always produce the same result, no matter your Browserslist configuration. Whoops!

In reality, the issue is that ESBuild doesn’t support Browserslist and uses its own fine-grained target system 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), or use the following fix.

The fix in two steps

Fortunately, there’s the browserslist-to-esbuild package (by Marco Fugaro) 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.

  1. First, add browserslist-to-esbuild to your project (tested version: 1.2.0):
npm i -D browserslist-to-esbuild
Enter fullscreen mode Exit fullscreen mode
  1. Then, in your vite.config.js file:
import { defineConfig } from 'vite'
import browserslistToEsbuild from 'browserslist-to-esbuild'

export default defineConfig({
  build: {
    target: browserslistToEsbuild(),
  },
})
Enter fullscreen mode Exit fullscreen mode

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

(Note: another package 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 while I want to support Safari starting 12.2 (iOS) or 13.1 (macOS).

 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)
Enter fullscreen mode Exit fullscreen mode

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 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 config entry to set supported['top-level-await'] to true:

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,
    },
  },
}
Enter fullscreen mode Exit fullscreen mode

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, 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 by this situation.

On the other hand, if your project uses the @vitejs/plugin-legacy, an official Vite plugin helping you supporting browsers outside of Vite browser compatibility list, then Vite will make use of your Browserslist configuration because this plugin uses Babel @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 and even a ignorebrowserslistconfig option, 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 by @kingyue, there’s currently a bug in @vitejs/plugin-legacy (and a fix in review !), causing the Browserslist configuration to be ignored. (Fixed in @vitejs/plugin-legacy 4.0.1.)

Top comments (4)

Collapse
 
kingyue profile image
Yue JIN

@vitejs/plugin-legacy does not use browserslistrc currently: github.com/vitejs/vite/issues/2476

Collapse
 
meduzen profile image
Mehdi M.

This has been fixed in @vitejs/plugin-legacy 4.0.1!

Collapse
 
meduzen profile image
Mehdi M.

I just tested in a project using @vitejs/plugin-legacy, and indeed the output remains the same no matter the Browserslist configuration. I’ve added a note at the end of the article. Thanks a lot!

Collapse
 
meduzen profile image
Mehdi M.

@iskin already opened a follow-up issue in Vite repository. ❤️