Vite is a modern tool for frontend development. One of Vite’s functionalities is to deploy the app for production, including bundling all JavaScript (JS) files. Although Vite supports building for a particular browser target, it only transforms syntax with the help of ebuild, but does not perform any JavaScript polyfill.

However, polyfill is essential for many projects. On the one hand, manual polyfill can be error-prone. On the other hand, an on-demand polyfill service like polyfill.io bears its own limitations. It is ideal to use a polyfill mechanism that is integrated with Vite.

In this post, we discuss how to automatically polyfill JavaScript APIs in a Vite project with the help of the Vite legacy plugin.

Overview: Automatic Polyfill with Vite

To automatically polyfill modern JS functions, install the Vite legacy plugin (@vitejs/legacy-plugin) and add the following to vite.config.ts if you use TypeScript, or vite.config.js if you use JS:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import { defineConfig } from "vite";
import legacy from "@vitejs/plugin-legacy";

const legacyPluginOptions = {
  modernTargets: "since 2023-01-01, not dead",
  modernPolyfills: true,
  renderLegacyChunks: false,
} as const;

export default defineConfig({
  plugins: [legacy(legacyPluginOptions)],
});

Also, remove as const if you use JS.

  • modernTargets: Specifies browser compatibility for polyfill in this property. It is a string or an array of strings that follow Browserslist. The example above refers to browsers that have been released since January 1, 2023 and are not dead. A browser is not dead if it has not been without official support or updates for 24 months.
  • modernPolyfills: Enables polyfill for modern JS functions.
  • renderLegacyChunks: Disables polyfill for legacy browsers. Depending on your situation, you may decide whether to include this line.

Demo App

To better illustrate how to include modern polyfills, we’ll walk through the process of building a demo app.

In this demo app, we will be targeting stable browser releases in January 2023. In other words, we would like the app to be usable on any stable versions of modern browsers that have been released since January 1, 2023.

Project Setup

  1. Create a new Vite project. Replace vanilla-ts with vanilla if you prefer vanilla JS over TypeScript.

    npm create vite@latest polyfill-app -- --template vanilla-ts
    
  2. Install project dependencies.

    cd polyfill-app && npm install
    
  3. If you use TypeScript, edit tsconfig.json and replace any "ES20*" in "target" and "lib" with "ESNext". This is to prevent the TypeScript compiler from not recognizing later modern functions.

  4. Add a modern JS function usage. In this demo, we’ll use Array.prototype.with, which is not available in stable versions of modern browsers released in January 2023. Edit src/counter.ts (or src/counter.js if you use vanilla JS) and replace counter = count with

    counter = [count].with(0, count)[0];
    

    This line does exactly what counter = count does. Its only purpose is to insert a use of Array.prototype.with.

If you run npm run build now, you will see no polyfill.

Add Automatic Polyfills

  1. Install the Vite legacy plugin:

    npm install --save-dev @vitejs/plugin-legacy
    
  2. Create the vite.config.ts (or vite.config.js if you use vanilla JS) file. Add the following content: (Remove as const if you use vanilla JS)

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    
    import { defineConfig } from "vite";
    import legacy from "@vitejs/plugin-legacy";
    
    const legacyPluginOptions = {
      modernTargets: "since 2023-01-01, not dead",
      modernPolyfills: true,
      renderLegacyChunks: false,
    } as const;
    
    export default defineConfig({
      plugins: [legacy(legacyPluginOptions)],
      build: {
        target: ["chrome109", "edge109", "firefox109", "ios16.3", "safari16.3"],
      },
    });
    

The Overview section has already explained most of the content of this file. We also filled the build.target option with browsers released in January 2023 so that syntax transformation also aligns with our intended supported browsers.

Running npm run build now, you should see Vite generates a polyfill JS file. Running DEBUG='vite:legacy' npm run build reveals which functions are polyfilled:

[@vitejs/plugin-legacy] modernTargets: since 2023-01-01, not dead
vite v5.2.12 building for production...
✓ 7 modules transformed.
rendering chunks (1)...[@vitejs/plugin-legacy] modern polyfills: Set(1) { 'core-js/modules/es.array.with.js' }
dist/index.html                     0.54 kB │ gzip: 0.32 kB
dist/assets/index-DxRda7M6.css      1.22 kB │ gzip: 0.63 kB
dist/assets/index-DWjTM3Sh.js       3.07 kB │ gzip: 1.65 kB
dist/assets/polyfills-B1wrU7sj.js  10.13 kB │ gzip: 4.44 kB
✓ built in 655ms

Also, check out the demo source code on GitHub and GitLab.