How to load polyfills only when needed (2024)

A colleague asked me:

These days, how do you typically serve polyfills only to browsers that need them?

I know three ready-to-use approaches for that:

  • polyfill.io

  • module/nomodule

  • Babel’s useBuiltIns

polyfill.io is a service that inspects the browser’s User-Agent and serves a script with polyfills targeted specifically at that browser.

With polyfill.io, you add a single script in front of your bundle:

<script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script><script src="/bundle.min.js"></script>

and the script serves exactly the polyfills the visitor needs.

polyfill.io also supports picking a subset of polyfills. This is useful when, out of all modern JS features, you only use a few – e.g. Map and Promise – and don’t want to burden IE11 users with extra code.

Tricky parts

  1. The polyfill.io script will add 50-300 ms to your Time to Interactive. The script is (obviously) hosted on a server different from yours, and loading stuff from a different server is costly. The browser will have to spend extra 50-300 ms to setup a connection to the server, and this means adding 50-300 ms to your Time to Interactive.

    (Well, unless you resort to self-hosting or complex CDN hacks.)

  2. The polyfill.io script might add these 50-300 ms to your First Contentful Paint as well – if you put it into <head> without an async or a defer attribute.

  3. In the (unlikely) event of a polyfill.io outage, your site will either get very slow, or will break in older browsers. The outage is unlikely because polyfill.io relies on a CDN and has never gone down so far; but keep this in mind.

module/nomodule is a pattern when you serve scripts for modern browsers with <script type="module">, and scripts for older browsers with <script nomodule>:

<!-- Full polyfill bundle for old browsers --><script nomodule src="/polyfills/full.min.js"></script><!-- Smaller polyfill bundle for browsers with ES2015+ support --><script type="module" src="/polyfills/modern.min.js"></script><!-- Bundle script. `defer` is required to execute this script after the `type="module"` one --><script src="/bundle.min.js" defer></script>

This pattern relies on the fact that old browsers – ones that don’t support ES2015 – will not load type="module" scripts – and will load nomodule ones. Which means you can use nomodule to serve ES2015 polyfills exactly to browsers that need them!

So, in the snippet above:

Philip Walton wrote a great detailed article about the module/nomodule approach.

There’s a bunch of guides and plugins for bundles and frameworks that help to implement the module/nomodule pattern, e.g.:

Tricky parts

  1. Safari 10.1 has a quirk. It supports type="module" but doesn’t support the nomodule attribute. If you support this Safari version, you’ll have to work around that.

  2. The module/nomodule patters draws a split only between ES5 and ES2015+ browsers. ES2016 and newer standards added a bunch of other polyfillable features like Array.prototype.includes() or Object.values(). You’ll have to serve their polyfills to all ES2015+ browsers – even though most of these browsers won’t need them.

  3. type="module" scripts are always deferred. If you want to execute a type="module" polyfill before the regular bundle script, you have to add the defer tag to the bundle as well.

@babel/preset-env has an option called useBuiltIns. With this option, you can make Babel cherry-pick polyfills for specific browsers:

// .babelrc{ "presets": [ ["env", { // Specify browsers you’re targeting... "targets": "> 0.25%, not dead", // ...and either... "useBuiltIns": "entry", // ...or "useBuiltIns": "usage" }] ]}

With useBuiltIns: "entry", Babel will replace the import of core-js – the most common polyfill library – with specific polyfills required for browsers you’re targeting. So if you’re targeting only the latest Chrome and Firefox, @babel/preset-env will strip unnecessary polyfills for you:

// Before → 293 polifylls bundledimport 'core-js';// After → 87 polyfills bundledimport 'core-js/modules/es.array.unscopables.flat';import 'core-js/modules/es.array.unscopables.flat-map';// ...

With useBuiltIns: "usage", Babel will go even further and only add polyfills for methods you actually use:

// Before → no polyfills bundledconsole.log([5, 6, 7].includes(5));// After → with `targets: "IE 11"` → 1 polyfill bundledimport 'core-js/modules/es.array.includes';console.log([5, 6, 7].includes(5));// After → with `targets: "Chrome >=70"` → 0 polyfills bundledconsole.log([5, 6, 7].includes(5));

Tricky parts

  1. useBuiltIns: "entry" is not very useful if you’re targeting old browsers, like IE 11. It might remove some polyfills, like Object.getPrototypeOf, but most of them will stay in the bundle and would still be downloaded by everyone.

  2. If you use core-js 2, useBuiltIns: "usage" will fail to add some of the newer polyfills. For example, it won’t polyfill this code:

    [].flat();

    because it won’t know that .flat() is a method that requires polyfilling.

    To solve this, upgrade to core-js 3 which includes the latest polyfills.

  3. useBuiltIns: "usage" will not add polyfills for your dependencies – unless you pipe node_modules through Babel. So if you aren’t cautious enough, you might get runtime error in older browsers.

  4. In some cases, useBuiltIns: "usage" will add excessive polyfills. For example, with this code:

    import { myVar } from './myModule';myVar.includes();

    @babel/preset-env has no way of knowing whether myVar is an array or a string – so it’d bundle polyfills both for Array.prototype.includes and String.prototype.includes.

All three widely supported solutions have their benefits and drawbacks:

  • polyfill.io → very easy to setup and doesn’t ship anything to modern browsers – but costly in terms of TTI (and, sometimes, FCP)
  • module/nomodule → has wide tooling support but only strips ES2015− polyfills
  • Babel’s useBuiltIns: easy to setup for everyone who’s already using @babel/preset-env; but either not very useful if you’re targeting older browsers, or requires you to complile node_modules as well

The best solution would be a custom one: something that combines benefits of polyfill.io and Babel’s useBuiltIns but doesn’t incur their costs. To do this, you may:


Thanks to Nicoló Ribaudo for helping with the Babel section.

How to load polyfills only when needed (2024)

References

Top Articles
Forager: Complete List of Skills & Recommendations with Guide - PwrDown
Forager: Complete List of Quests, Guides & Rewards - PwrDown
Hotels Near 6491 Peachtree Industrial Blvd
Encore Atlanta Cheer Competition
Knoxville Tennessee White Pages
What spices do Germans cook with?
Linkvertise Bypass 2023
Top Financial Advisors in the U.S.
Unlocking the Enigmatic Tonicamille: A Journey from Small Town to Social Media Stardom
Decaying Brackenhide Blanket
Xm Tennis Channel
2024 U-Haul ® Truck Rental Review
Guidewheel lands $9M Series A-1 for SaaS that boosts manufacturing and trims carbon emissions | TechCrunch
Kitty Piggy Ssbbw
Patrick Bateman Notebook
Vistatech Quadcopter Drone With Camera Reviews
Everything you need to know about Costco Travel (and why I love it) - The Points Guy
Yard Goats Score
Espn Horse Racing Results
Johnnie Walker Double Black Costco
Lisas Stamp Studio
R. Kelly Net Worth 2024: The King Of R&B's Rise And Fall
Www.dunkinbaskinrunsonyou.con
Ceramic tiles vs vitrified tiles: Which one should you choose? - Building And Interiors
How Taraswrld Leaks Exposed the Dark Side of TikTok Fame
Skymovieshd.ib
Publix Near 12401 International Drive
Black Panther 2 Showtimes Near Epic Theatres Of Palm Coast
Nearest Ups Ground Drop Off
WPoS's Content - Page 34
Stickley Furniture
Past Weather by Zip Code - Data Table
How to Use Craigslist (with Pictures) - wikiHow
Rush County Busted Newspaper
Pixel Combat Unblocked
Chadrad Swap Shop
Memberweb Bw
Diana Lolalytics
Personalised Handmade 50th, 60th, 70th, 80th Birthday Card, Sister, Mum, Friend | eBay
Tsbarbiespanishxxl
Directions To The Closest Auto Parts Store
Thor Majestic 23A Floor Plan
Craigslist Farm And Garden Reading Pa
Cocorahs South Dakota
Cuckold Gonewildaudio
What is a lifetime maximum benefit? | healthinsurance.org
Tito Jackson, member of beloved pop group the Jackson 5, dies at 70
116 Cubic Inches To Cc
Evil Dead Rise - Everything You Need To Know
F9 2385
Adams County 911 Live Incident
7 National Titles Forum
Latest Posts
Article information

Author: Roderick King

Last Updated:

Views: 6498

Rating: 4 / 5 (51 voted)

Reviews: 82% of readers found this page helpful

Author information

Name: Roderick King

Birthday: 1997-10-09

Address: 3782 Madge Knoll, East Dudley, MA 63913

Phone: +2521695290067

Job: Customer Sales Coordinator

Hobby: Gunsmithing, Embroidery, Parkour, Kitesurfing, Rock climbing, Sand art, Beekeeping

Introduction: My name is Roderick King, I am a cute, splendid, excited, perfect, gentle, funny, vivacious person who loves writing and wants to share my knowledge and understanding with you.