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.
This article seems kind of odd. It talks about writing with “ES6 syntax” but then goes on to talk about “polyfilling features.” No amount of polyfilling, however targeted, will get you arrow functions, const, let, or other syntax in older browsers.
I’m extremely confused by this as well. So what do we do with the ES2015+ syntax? Do we still compile that, but somehow without ES2015+ libraries bundled in? But then how do we make that work with module loaders? :X
What might be really cool is a way to load ES2015 code directly in supporting browsers (syntax and all) while still delivering ES5 for non-supporting (or partially-supporting) browsers. Still not sure how module loaders would work, though. And it would double the debugging effort, since there are two different execution paths depending on support or lack thereof.
I think there is a confusion here between ES6 syntax, which can’t be polyfilled and will throw errors on older browsers, and new JS API which can be polyfilled.
If you want to support older browsers, you can’t skip the ES6 to ES5 transpiling.
This seems completely useless to me. So you waste an extra http request to go and ask a third party API for polyfills for Array.values or String.contains, but you still have to code in ES5, i.e. without arrow functions, default parameters, destructuring, etc etc. With the risk that if their site is down or just slow you are screwed.
Sorry but I really don’t see the point.
yep, this sounds like it will only increase fragmentation of ES versions further. You’ll have ES6, transpiled ES6, ES6 (but not with classes, =>, etc.), ES5, modernizr…
Why don’t we just do feature detection and trigger differential loading of a transpiled or ES6 library dynamically? This could be a polyfill in the root document (if doing an SPA, else this is going to be more convoluted), so we don’t need to waste precious requests.
Something along the lines of (very quick and dirty)
I still think that until es6 imports are supported natively, downloading a few kB of babel polyfills it’s perfectly acceptable in most practical cases.
After reviewing the code in the library, it doesn’t even work correctly… It doesn’t even have any tests in the repo.
This post should’ve been reviewed and code tested to work correctly by Chris or someone else on the CSS-Tricks team.
In case you’ll encounter future problems in any regard:
“It’s not working!” does not really help anybody. :)
It seems to work for me just fine. What’s the problem you have?
thank you http://www.altunyusufelihaliyikama.com
Hi Pascal Klau,
Thanks for sharing this, documentation looks very helpful and attractive to use and we love to use it our projects too, but I found nothing is written in the reference files all those file are blank, https://polyfill.io/v2/polyfill.min.js or https://polyfill.io/v2/polyfill.js, is that dummy url?
When I browse https://polyfill.io/v2/polyfill.min.js, I got below message
and When I removed min from URL as per provided instructions it give me below message
Could you please share more details, in case any recent changes has been made in url?
Thanks in advance :)
I think that’s kinda the point. It’s figured out that you’re in Chrome 55 and don’t need any polyfills. (But you still had to make the HTTPRequest to figure it out)