Polyfill JavaScript Only When You Need To

The following is a guest post by Pascal Klau, a trainee from South Germany, who dislikes unnecessary HTTP requests and broccoli. Pascal is going to explain way to use a polyfilling service in such a way that you might get away with not using it at all.

The Situation

We want to write JavaScript in the ES6 syntax. But since we need to support older browsers that don’t understand ES6, we need to deal with that.

Here the standard procedure: Write ES6 → compile everything down to ES5 (e.g. Babel) → send that to the browser.

This may not be the most efficient way anymore. The problem is that we’re forcing modern browsers to run the old code when they don’t have to. They support ES6, so can’t we give them ES6.

A Better Way

There is a polyfill project called Polyfill.io API that can polyfill ES6 code client side.

It also implements polyfills for some HTML features like the <picture> element.

Description from their website:

Polyfill.io reads the User-Agent (UA) header of each request and returns polyfills that are suitable for the requesting browser. Tailor the response based on the features you’re using in your app […]

It is being developed by Financial Times, so it has some support and we can be fairly confident it’s not going to disappear on us.

One thing to be clear on: Polyfill.io does not provide support for syntactic sugar. For example, Classes, enhanced Object Literals, and things like Arrow Functions. You’d still need a compiler for those.

Set up Polyfill.io

Adding Polyfill.io to your project can be this simple. Add the CDN-hosted script to your page:

<script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>

Running the script spits out the UA and what features you want.

UA detected: chrome/56.0.0
Features requested: default

Change request parameters

There are a bunch of options to customize the request which you put into the script source.

Features

List of browser features to polyfill. Accepts a comma-separated list of feature names. Available feature names are shown on the Browsers and Features page.

<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=fetch"></script>

In Safari 10 the script now says something like this:

Features requested: fetch

- setImmediate, License: CC0 (required by "Promise", "fetch")
- fetch

If a feature like fetch depends on another feature like Promise, Polyfill.io automatically adds it.

Flags

  • always – Polyfill should be included regardless of whether it is required by the user-agent making the request.
  • gated – If the polyfill is included in the bundle, it will be accompanied by a feature detect, which will only execute the polyfill if the native API is not present.
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=fetch&flags=gated"></script>

Callback

Name of a function to call after the polyfills are loaded. It is simply a way of triggering your own code when the polyfills have loaded, intended to allow the polyfill service to be more easily loaded asynchronously with async and defer attributes.

The Problem

As good as all this sounds, it is still not perfect.

Modern browsers do not have to load ES5 now, but instead have to make a server roundtrip (an HTTP Request) to check if polyfills are needed.

This bugged me so much that I worked on a little project to help.

An Even Better Way

Set up dynamic polyfill

The npm package I created is called dynamic-polyfill. It checks whether the feature is natively supported or not before making any server request.

The setup looks like this:

import polyfill from 'dynamic-polyfill'

polyfill({
    fills: 'fetch, Promise',
    options: 'gated', // default: null
    minify: false,  // default: true
    afterFill() {
        main()
    }
})

function main() {
    // app code here
}

That will execute essentially like this, in plain language:

Do [window.fetch, window.Promise] exist?

If they do, run the afterFill() callback.

If they do not, create a <script> tag with async attribute, insert the Polyfill.io link with all the provided options and run the afterFill() callback after it has finished loading.

Note: Not all options are support yet, only the most important ones.

Small footprint

Since this module is less than 1KB minified and does not have any dependencies, it has a super small footprint in your project.

Conclusion

Write future-proof and efficient JavaScript for modern browsers. Let Polyfill.io handle older browsers. Don’t do the extra HTTP Request if you don’t have to.

Just make sure you have something handy to dry these tears of joy.