An easier way of using polyfills

Polyfills are a fantastic way to enable the use of modern code even while supporting legacy browsers, but currently using polyfills is too hard, so at the FT we’ve built a new service to make it easier. We’d like to invite you to use it, and help us improve it.

Image from https://www.flickr.com/photos/hamur0w0/6984884135

More pictures, they said. So here’s a unicorn, which is basically a horse with a polyfill.

The challenge

Here are some of the issues we are trying to solve:

  • Developers do not necessarily know which features need to be polyfilled. You load your site in some old version of IE beloved by a frustratingly large number of your users, see that the site doesn’t work, and have to debug it to figure out which feature is causing the problem. Sometimes the culprit is obvious, but often not, especially when legacy browsers also lack good developer tools.
  • There are often multiple polyfills available for each feature. It can be hard to know which one most faithfully emulates the missing feature.
  • Some polyfills come as a big bundle with lots of other polyfills that you don’t need, to provide comprehensive coverage of a large feature set, such as ES6. It should not be necessary to ship all of this code to the browser to fix something very simple.
  • Newer browsers don’t need the polyfill, but typically the polyfill is served to all browsers. This reduces performance in modern browsers in order to improve compatibility with legacy ones. We don’t want to make that compromise. We’d rather serve polyfills only to browsers that lack a native implementation of the feature.

Our solution: polyfills as a service

To solve these problems, we created the polyfill service. It’s a similar idea to going to an optometrist, having your eyes tested, and getting a pair of glasses perfectly designed to correct your particular vision problem. We are doing the same for browsers. Here’s how it works:

  1. Developers insert a script tag into their page, which loads the polyfill service endpoint.
  2. The service analyses the browser’s user-agent header and a list of requested features (or uses a default list of everything polyfillable) and builds a list of polyfills that are required for this browser
  3. The polyfills are ordered using a graph sort to place them in the right dependency order.
  4. The bundle is minified and served through a CDN (for which we’re very grateful to Fastly for their support)

Do we really need this solution? Well, consider this: Modernizr is a big grab bag of feature detects, and all sensible use cases benefit from a custom build, but a large proportion of Modernizr users just use the default build, often from cdnjs.com or as part of html5boilerplate. Why include Modernizr if you aren’t using its feature detects? Maybe you misunderstand the purpose of the library and just think that Modernizr “fixes stuff”? I have to admit, I did, when I first heard the name, and I was mildly disappointed to find that rather than doing any actual modernising, Modernizr actually just defines modernness.

The polyfill service, on the other hand, does fix stuff. There’s really nothing wrong with not wanting to spend time gaining intimate knowledge of all the foibles of legacy browsers. Let someone figure it out once, and then we can all benefit from it without needing or wanting to understand the details.

How to use it

The simplest use case is:

<script src="//cdn.polyfill.io/v1/polyfill.min.js" async defer></script>

This includes our default polyfill set. The default set is a manually curated list of features that we think are most essential to modern web development, and where the polyfills are reasonably small and highly accurate. If you want to specify which features you want to polyfill though, go right ahead:

<!-- Just the Array.from polyfill -->
<script src="//cdn.polyfill.io/v1/polyfill.min.js?features=Array.from" async defer></script>
 
<!-- The default set, plus the geolocation polyfill -->
<script src="//cdn.polyfill.io/v1/polyfill.min.js?features=default,Navigator.prototype.geolocation" async defer></script>

If it’s important that you have loaded the polyfills before parsing your own code, you can remove the async and defer attributes, or use a script loader (one that doesn’t require any polyfills!).

Testing and documenting feature support

This table shows the polyfill service’s effect for a number of key web technologies and a range of popular browsers:

Polyfill service support grid

The full list of features we support is shown on our feature matrix. To build this grid we use Sauce Labs’ test automation platform, which runs each polyfill through a barrage of tests in each browser, and documents the results.

So, er, user-agent sniffing? Really?

Yes. There are several reasons why UA analysis wins out over feature detection for us:

  • In some cases, we have multiple polyfills for the same feature, because some browsers offer a non-compliant implementation that just needs to be bashed into shape, while others lack any implementation at all. With UA detection you can choose to serve the right variant of the polyfill.
  • With UA detection, the first HTTP request can respond directly with polyfill code. If we used feature detection, the first request would serve feature-detect code, and then a second one would be needed to fetch specific polyfills.

Almost all websites with significant scale do UA detection. This isn’t to say the stigma attached to it is necessarily bad. It’s easy to write bad UA detect rules, and hard to write good ones. And we’re not ruling out making a way of using the service via feature-detects (in fact there’s an issue in our tracker for it).

A service for everyone

The service part of the app is maintained by the FT, and we are working on expanding and improving the tools, documentation, testing and service features all the time. The source is freely available on GitHub so you can easily host it yourself, but we also host an instance of the service on cdn.polyfill.io which you can use for free, and our friends at Fastly are providing free CDN distribution and SSL.

We’ve made a platform. We need the community’s help to populate it. We already serve some of the best polyfills from Jonathan Neal, Mathias Bynens and others, but we’d love to be more comprehensive. Bring your polyfills, improve our tests, and make this a resource that can help move the web forward!

About Andrew Betts

Director of FT Labs, which develops and promotes experimental web technologies at the Financial Times

More articles by Andrew Betts…

About Robert Nyman [Editor emeritus]

Technical Evangelist & Editor of Mozilla Hacks. Gives talks & blogs about HTML5, JavaScript & the Open Web. Robert is a strong believer in HTML5 and the Open Web and has been working since 1999 with Front End development for the web - in Sweden and in New York City. He regularly also blogs at http://robertnyman.com and loves to travel and meet people.

More articles by Robert Nyman [Editor emeritus]…


11 comments

  1. Petter

    UA sniffing is ok, but only as an initial guess. I think it should be combined with actual feature testing in the downloaded code.

    In 99% of downloads, this will only add insignificant overhead, but in the 1% of cases where it matters, the site will not break if there is a fallback through feature testing.

    November 6th, 2014 at 06:51

    1. Bart

      Percentages are moot if Murphy’s Law demands the client’s computer has weird UA string. It won’t be the first time someone has been messing with developer tools or privacy plugins (and then forgot about it).

      November 7th, 2014 at 11:38

  2. Conor Luddy

    Nice work guys, looks very interesting!

    November 6th, 2014 at 07:02

  3. Alfonso

    It would be much better if each link in the feature matrix linked to a page describing what each feature does as well as what’s the polyfill(s) being used, so we can understand better if we want it or not.
    I’m really wondering why there’s a “Window” polyfill and why IE and Safari need an “Event” polyfill.

    November 6th, 2014 at 09:52

  4. mario

    looks useful, really useful

    November 6th, 2014 at 12:09

  5. Dorian

    anyone using it in production?

    November 6th, 2014 at 13:17

  6. Brenton

    The projects I’m working on now are isomorphic (that is, they render both in Node and in the browser). [jsx-loader](https://github.com/petehunt/jsx-loader) is awesome for supporting ES6 syntaxes, but it isn’t responsible for making sure that Array, Object, Math, et-al are polyfilled.

    What if this was a thing:

    “`
    // polyfill.io.js

    if (global.hasOwnProperty(“window”)) {
    if (!window.hasOwnProperty(“__polyfillIOVersion”)) {
    console.warn(“polyfill.io not found. Make sure to include at the top of your !”)
    }

    } else {
    if (!process.version.contains(“0.11.13”))
    console.warn(“You are using an old version of the polyfill npm module – please update it.”)

    // augment Array, Object, Math, etc.’s prototypes here
    }
    “`

    that I could use in my isomorphic app by simply `require(“polyfill.io”)` from my app (and adding the corresponding “ tag to my markup)?

    November 6th, 2014 at 15:08

  7. Farid Nouri Neshat

    It’d be interesting to know how much content will be served to each browser, so that we would know if we should avoid the default list or not.

    November 7th, 2014 at 05:09

  8. Oisin G.

    Great stuff guys – I noticed that you’re not consistent about the script tag to include. On the “About” page, you’re specifying:

    src=”https://cdn.polyfill.io/…”

    where you probably should be saying:

    src=”//cdn.polyfill.io/…”

    Keep it up!

    November 10th, 2014 at 09:03

  9. ingee

    Wonderful article.
    BTW, what was the first image? It seems that the image link is broken.

    http://blog.mozilla.org/hacks/files/2014/10/6984884135_8823c68f9d_o-e1414691132239-500×250.jpg

    November 27th, 2014 at 10:25

    1. Robert Nyman [Editor]

      It’s fixed now, thanks for letting us know!

      November 28th, 2014 at 10:33

Comments are closed for this article.