Photo from freestocks.org on Unsplash

No-brainer Polyfill Solution for Single Page Applications

One of the more annoying parts of web development is supporting older browsers, and if web developers agree on one thing, it’s that you should be rewarding users for being in a modern browser. At least, I hope.

Typically this means not loading polyfills if you don’t need to. Unfortunately, a common approach is to load @babel/polyfill (or something similar) as part of your entry point. While this works, it doesn’t allow you to control why it shows up, only that it should.

I think this is wrong. It punishes users in modern, ES2015+ capable browsers by adding anywhere from 1kb to sometimes 100kb+ (depending on how much polyfill you need) of extra scripts over the network. This may be imperceptible for users on high speed internet, but that’s seconds added on for 3G connections. Yikes.

Just Be Lazy About It

The solution I see as simplest and configuration-free is lazy loading the polyfill with a Promise:

import "core-js/features/promise"const polyfills = []
const modernBrowser = "fetch" in window && "assign" in Object
if (!modernBrowser) {
polyfills.push(
import(/* webpackChunkName: "polyfill" */ "core-js/stable")
)
}
Promise.all(polyfills)
.then(() => import(/* webpackChunkName: "app" */ "./app"))
.catch(error => console.error("Uh oh! Polyfills couldn't load!", error))

But you’re polyfilling Promise before you load the polyfill!

It’s 1kb to your core bundle, and without it, you can’t do the Promise.all check at the end. A small price to pay for this strategy.

What’s in modernBrowser?

With the above code, you’re only ever going to load Polyfills if the browser has access to Object.assign and window.fetch. Tailor this check to what your code actually uses; I’m only using these as examples. Another good one is probably window.Symbol if you use React, for example.

Which polyfill do I use?

Here, I’m using core-js/stable, which is more or less the same set of features loaded from babel polyfill. There’s plenty of other options and presets depending on your needs.

Won’t the Promise slow down my app on initial load?

By a few milliseconds, maybe. However, if the user is in a modern browser, polyfills here will be an empty array and get skipped, immediately invoking then in the Promise. Again, a small price to pay for saving even more time in loading kilobytes of unnecessary polyfill.

What if I want more than one polyfill/feature?

Just keep on pushing those imports to your polyfills array.

if (!window.Symbol) {
polyfills.push(
import(/* webpackChunkName: "polyfill.symbol" */ "core-js/features/symbol")
)
}
if (!Object.assign) {
polyfills.push(
import(/* webpackChunkName: "polyfill.object-assign" */ "core-js/features/object/assign")
)
}

I think ultimately, your implementation of a polyfill is up to you and your needs. Hopefully this gives you new ideas!

George is a front-end developer and digital designer living in Oakland, California. By day, he’s a front-end software engineer at Scribd. Other times, he can be found long boarding, playing video games, or napping. Usually napping.

Learning to see. @gwtrev