惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

大猫的无限游戏
大猫的无限游戏
K
Kaspersky official blog
Apple Machine Learning Research
Apple Machine Learning Research
B
Blog
aimingoo的专栏
aimingoo的专栏
M
MIT News - Artificial intelligence
小众软件
小众软件
云风的 BLOG
云风的 BLOG
腾讯CDC
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Hugging Face - Blog
Hugging Face - Blog
S
SegmentFault 最新的问题
Stack Overflow Blog
Stack Overflow Blog
量子位
S
Secure Thoughts
G
GRAHAM CLULEY
C
CXSECURITY Database RSS Feed - CXSecurity.com
人人都是产品经理
人人都是产品经理
雷峰网
雷峰网
T
Threat Research - Cisco Blogs
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Cisco Talos Blog
Cisco Talos Blog
G
Google Developers Blog
爱范儿
爱范儿
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
有赞技术团队
有赞技术团队
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Martin Fowler
Martin Fowler
The GitHub Blog
The GitHub Blog
Google DeepMind News
Google DeepMind News
C
Cisco Blogs
D
Darknet – Hacking Tools, Hacker News & Cyber Security
博客园 - 聂微东
宝玉的分享
宝玉的分享
H
Hackread – Cybersecurity News, Data Breaches, AI and More
N
Netflix TechBlog - Medium
Forbes - Security
Forbes - Security
Engineering at Meta
Engineering at Meta
S
Security Affairs
Help Net Security
Help Net Security
博客园 - 三生石上(FineUI控件)
AWS News Blog
AWS News Blog
博客园 - 叶小钗
Recent Commits to openclaw:main
Recent Commits to openclaw:main
V2EX - 技术
V2EX - 技术
Hacker News: Ask HN
Hacker News: Ask HN
Project Zero
Project Zero
H
Heimdal Security Blog
W
WeLiveSecurity
C
Check Point Blog

Philip Walton

Dynamic LCP Priority: Learning from Past Visits Performant A/B Testing with Cloudflare Workers My Challenge to the Web Performance Community Smaller HTML Payloads with Service Workers Cascading Cache Invalidation Using Native JavaScript Modules in Production Today KV Storage: the Web's First Built-in Module Idle Until Urgent Page Lifecycle API First Input Delay Responsive Components: a Solution to the Container Queries Problem Why Web Developers Need to Care about Interactivity Deploying ES2015+ Code in Production Today How We Track Pageviews Is All Wrong The Google Analytics Setup I Use on Every Site I Build The Dark Side of Polyfilling CSS Loading Polyfills Only When Needed Untangling Deeply-Nested Promise Chains Learning How to Set Up Automated, Cross-browser JavaScript Unit Testing Houdini: Maybe the Most Exciting Development in CSS You've Never Heard Of Why I'm Excited About Native CSS Variables Do We Actually Need Specificity In CSS? How to Become a Great Front-End Engineer Extending Styles Side Effects in CSS Normalizing Cross-browser Flexbox Bugs Measuring Your Site's Responsive Breakpoint Usage The Dangers of Stopping Event Propagation Stop Copying Social Code Snippets Implementing Private and Protected Members in JavaScript How to Find Qualified Developers Interviewing as a Front-End Engineer in San Francisco Solved by Flexbox Decoupling Your HTML, CSS, and JavaScript Why I Test Private Functions In JavaScript How to Unit Test Private Functions in JavaScript Introducing HTML Inspector CSS: Everything is Global and How to Deal With It Dynamic Selectors Defending Presentational Class Names The Future of OOCSS: A Proposal What No One Told You About Z-Index CSS Architecture
The State of ES5 on the Web
For library authors · 2024-09-08 · via Philip Walton

In 2017, I wrote an article showing web developers how they could deploy ES6+ code (a.k.a. ES2015) to production, without needing to transpile it to ES5. This technique was liberating for website developers who wanted the freedom to write modern code without having to worry about transpiler or polyfill bloat.

Unfortunately, while many website developers were able to use this technique, most JavaScript library authors could not.

Library authors face a lot more constraints than website developers, because they don’t control how their code is deployed. Also, since many of the popular build tools recommend developers exclude their node_modules directory from transpilation, library authors were forced to be extremely conservative—usually transpiling all the way back to ES5 in order to avoid potentially breaking sites.

But that was seven years ago, and there’s been a ton of advancement in the JavaScript tooling space since then. The browser landscape has also changed tremendously. Most notably IE 11, the last remaining ES5 browser, stopped being supported by Microsoft in 2022, which meant many businesses could finally stop supporting it as well.

So what is the current state of ES5 on the web today? And what are the best practices for web developers when building code for production?

This article looks at the data we have to answer these questions. It also offers some concrete recommendations (based on that data) for how both website developers and library authors should approach legacy browser support moving forward.

Quick disclaimer

Before digging into the real-world data on ES5 usage, I want to clarify that there is nothing inherently wrong with either authoring or publishing ES5 code.

JavaScript engines have been optimizing for ES5 code for much longer than they have for modern code, so if you have old ES5 code that is still working, there’s no reason to update it just to make it “modern”.

However, if you’re authoring code in ES6+ syntax and then using a build tool to transpile it to ES5, that generally results in a lot of polyfill and transpiler bloat that can significantly increase the size of your final bundles.

To illustrate this point, here’s an example:

console.log([1, 2, 3].at(-1));

If you transpile this code to ES5 by hand, it would probably look something like this:

var arr = [1, 2, 3];
console.log(arr[arr.length - 1]);

However, if you transpile this single line of code with Babel and configure it to add polyfills—even if you limited it to just the needed polyfills based on usage in the source code—it includes 71 core-js dependencies and goes from being 31 bytes to 11,217 bytes minified!

The point of this example is not to shame Babel or core-js. Those tools need to be able to support all possible ES6+ code, which requires them to account for all sorts of edge cases (though this particular example doesn’t have any).

Instead, the point is to emphasize that choosing to support legacy browsers does come with a cost, and that cost can be significant.

Unfortunately, the problem is actually worse than just code bloat. If you look at the data below on how popular websites today are actually transpiling and deploying their code to production, it turns out that most sites on the internet ship code that is transpiled to ES5, yet still doesn’t work in IE 11—meaning the transpiler and polyfill bloat is being downloaded by 100% of their users, but benefiting none of them.

The data

To understand the state of ES5 on the web, you have to look at three things, since all of these play a critical role in the final output of code that we, as web users, receive:

  • The default configurations of popular bundlers and build tools
  • The state of code found in popular JavaScript libraries
  • The state of code deployed by website owners

Default bundler and build tool configurations

Most bundlers and build tools are extremely configurable and offer almost unlimited control over the final output of code. However, in practice, most developers just use the defaults, so the defaults matter a great deal.

What are those defaults? Specifically, do the defaults result in code being transpiled to ES5?

To answer that question I took a look at the output generated by some of the most popular build tools, according to the most recent State of JS survey (2023), ordered roughly by popularity:

ToolDefaults to ES5?Notes
BrowserslistNoNot a build tool itself, but used by many build tools internally and is the most popular open source tool for configuring browser support targets. The defaults setting no longer includes any ES5 browsers. The last one was IE 11, which was marked as dead in version 4.21.
BabelYesBabel's documentation recommends setting a targets option (which uses Browserslist), but if none is specified it will transpile all code to ES5.
webpackNoBy default, webpack does not transpile any code. Most webpack users include babel-loader, and webpack's usage example for that suggests setting targets: "defaults".
TypeScript (tsc)YesTypeScript's default target option is ES5.
Next.jsNoNext.js uses Babel to transpile and by default sets a Browserslist config that targets "modern browsers" (i.e. browsers that support ES modules).
esbuildNoesbuild does not transpile by default. You can set a custom target to enable transpiling, but ES5 is not supported as a transpile target.
ViteNoVite uses esbuild and by default sets custom targets for "modern browsers" (i.e. browsers that support ES modules). Vite allows users to install a plugin if they need to support legacy browsers.
RollupNoRollup does not transpile by default. Many Rollup users install @rollup/plugin-babel, in which case the Babel defaults are used.
ParcelNoParcel automatically applies differential serving with customizable targets.
Closure CompilerNoDefaults to ECMASCRIPT_NEXT, which is the latest set of stable ES features.

As this table shows, the vast majority of bundlers and build tools no longer transpile to ES5 by default. It’s also notable that newer tools do not support ES5 at all, which shows that the trend is moving in that direction.

That said, Babel is still the most popular tool to transpile JavaScript, and as a result transpiling to ES5 is still quite common on the web (see ES5 usage in the wild below for more details).

In addition to looking at the popular build tools, I also looked at some of the most popular libraries in use today (again based on the State of JS survey, in rough popularity order):

To test each of these libraries, I created a bundle entry point that only imported that specific library, using one of the code examples from the library’s documentation. I then bundled the code using both Rollup and Webpack to test the output and see if it included any ES6+ syntax (specifically, any ES6+ syntax that isn’t supported in IE 11).

Here’s what I found:

LibraryContains ES6+ syntax?Notes
LodashNoES5 only
ReactNoES5 only
date-fnsYesarrow functions
three.jsYesasync/await, arrow functions, spread, destructuring
d3Yesarrow functions, spread, destructuring
Framer-motionYesarrow functions, spread, destructuring
greensockNoES5 only
dayjsNoES5 only
ZodYesasync/await, arrow functions, spread, destructuring
RxJSYesarrow functions
immerYesarrow functions, spread, destructuring
luxonYesasync/await, arrow functions, spread, destructuring
react-queryNoES5 only (bundles Babel helpers)

As the results above show, many popular JavaScript libraries are now publishing ES6+ syntax.

This is notable because, as I mentioned earlier, most developers that use Babel to transpile their source files while bundling, explicitly configure their bundler to not transpile anything in the node_modules directory—which was the main reason library authors historically felt they needed to continue transpiling to ES5.

For example, as of when this article was published (September 2024):

And TypeScript (tsc), the second-most popular transpilation tool after Babel, will only transpile a project’s own code files. It will not transpile project dependencies in node_modules.

This creates a problem for any website that wants to support ES5 and is using Babel or tsc to transpile their code. Unless they have a sophisticated understanding of how all the pieces of their build pipeline interact with each other, and unless they know how to properly configure each of them, they’re likely bundling ES6+ code in with their ES5 code without realizing it.

So this raises the question, is this actually causing a problem for real websites, or are most of them properly configuring their tools? The next section looks at data from HTTP Archive to answer that question.

Note: some of the libraries in the table above publish both ES5 and ES6+ versions, typically with the ES5 version set on the package.main field, and the ES6+ version set on either the package.module or package.exports fields. In these cases I only looked at whatever version of the script was getting pulled in by the bundler when using the default configuration (since that’s what most people use) and bundlers today default to using package.module or package.exports over package.main (see: [1], [2], [3]).

ES5 usage in the wild

The three main tools developers use to transpile ES6+ code to ES5 are:

  • Babel
  • TypeScript (tsc)
  • Closure Compiler (a.k.a. JSCompiler internally at Google)

All three of these tools include some form of polyfills and what are known as ES5 “helper” functions to avoid duplication in the final output. The most common ES5 helper function libraries used by these tools are: babel-helpers, core-js, regenerator-runtime, tslib, and $jscomp.

Many of the functions in these helper libraries are unique enough that it’s possible to detect (even in minified code) which sites are using them by querying the HTTP Archive. Searching for the presence of these helper functions—rather than standard ES5 syntax (such as var or non-arrow function)—helps to differentiate old ES5 code written by hand (usually fairly optimized) from newer ES5 code generated by a transpiler (usually fairly bloated).

I did a search on HTTP Archive to see how common it was for popular websites (top 10,000, based on CrUX popularity ranking) to include these helpers in the script bundles they deploy to production. I also wanted to see how common it was for sites to serve untranspiled ES6+ syntax.

Here’s what I found (full results):

  • 89% of sites serve at least 1 JavaScript file containing untranspiled ES6+ syntax.
  • 79% of sites serve at least 1 JavaScript file containing ES5 helper code.
  • 68% of sites serve at least 1 JavaScript file containing both ES5 helper code as well as untranspiled ES6+ syntax in the same file.

This last finding kinda blew my mind.

To reiterate what I said earlier—because it warrants repeating—if a browser does not support ES6+ syntax (such as IE 11), then it will error when trying to load a script file that contains ES6+ syntax. And if the browser does support ES6+ syntax, then it doesn’t need any of that ES5 helper code or any of the legacy polyfills. There is absolutely no reason to include both.

To double check that the results of this query were accurate, I manually tested 20 random sites on the list and confirmed that they do in fact include both the ES5 helper code as well as ES6+ syntax in some of the same script bundles. I also manually visited those sites in IE 11 and confirmed that those script bundles do indeed fail to load.

Keep in mind that these are not just random sites on the Internet. These are the 10,000 most popular websites in the world, which account for the vast majority of all web usage globally.

What does all this mean?

For a site to serve users code that contains both ES5 helpers and untranspiled ES6+ syntax, there’s really only two plausible explanations:

  1. The site doesn’t need to support ES5 browsers, but some of their dependencies transpile to ES5, so therefore ES5 code appears in their output.
  2. The site intended to support ES5 browsers, but they didn’t realize that some of their dependencies publish untranspiled ES6+ syntax, and they didn’t configure their bundler to transpile code in node_modules.

Regardless of the explanation, the fact that so many of the world’s most popular websites are serving so much unnecessary code, is a strong indicator that the defaults our tools currently recommend are not working.

If there’s any silver lining in this data, it’s that it’s pretty clear to me that dropping IE support is not going to have a noticeable impact on most businesses. If all of these major companies are apparently not impacted by these broken IE experiences, yours probably won’t be either.

Recommendations

The original rationale for why library authors should transpile to ES5 was that most sites needed to transpile to ES5 anyway. However, given that 89% of the top 10,000 websites currently ship some untranspiled ES6+ syntax, that rationale is no longer valid.

Given the data presented in this article, it definitely does not make sense for JavaScript library authors to be transpiling their code to ES5 anymore.

Practically speaking, library authors have no information about the browser support needs of the websites importing them, so it doesn’t make sense for them to be making that decision for all consumers of their library. At the same time, library authors shouldn’t assume that all consumers of their library will be able to run it through a sophisticated build process, so it’s important that their published code uses fully standard JavaScript and works in the current set of widely used browsers.

So what targets should library authors choose? In my opinion the best solution for library authors is to use Baseline—specifically to only include Baseline Widely Available features in any published code.

If you’re not familiar with Baseline, it’s an effort by the WebDX Community Group within the W3C to help developers easily identify features that are stable and well supported by all major browsers and browser rendering engines across desktop and mobile. A feature is considered Baseline Widely Available if it has been available in stable versions of all four major browsers for at least 30 months.

The main benefit of targeting something like Baseline Widely Available is that it’s a moving target, meaning it won’t get stuck in the past, like what happened with targeting ES5 (and what is currently happening with the esmodule target used by Next.js and Parcel).

Library authors can configure their build system to use Baseline Widely Available features now with the following Browserslist query (for any tool that supports Browserslist):

"browserslist": [
  "baseline widely available"
]

See Use Baseline with Browserslist for more details.

Update: since this post was originally published, Vite 7.0 was released and opted to use Baseline Widely Available as its default browser support target. This makes Vite a great option for library authors who don’t want to manually configure their own browser support targets.

If a site needs to support more browsers than those covered by Baseline Widely Available, that’s 100% fine. That site can always configure their build system to further transpile any libraries they’re importing. The point is this is a decision best made by the website developers, not the library author.

For website developers

The fact that so many popular websites ship both untranspiled ES6+ syntax and ES5 helpers in the same script bundle is a clear indication that the practice of excluding the node_modules directory from transpilation is not a good practice.

I’ve been arguing that this is not a good practice since 2017, but most developers I talked to didn’t want to follow this advice because doing so would slow down their builds.

These days, though, build tools have gotten significantly faster. Also, sites can configure their builds to only process code in node_modules when building for production. In development the code should run just fine on any browser that the developer is using, especially if library authors take the advice I gave above and target Baseline Widely Available.

Final thoughts

As I mentioned above, the point of this article is not to blame or shame websites or tool authors based on these findings. I also want to make sure it’s clear that I’m definitely not suggesting that websites should still be supporting IE 11. If anything, these findings suggest that supporting IE 11 is not a necessity for most businesses, even large ones with a global customer base.

The main points I want readers to take from this article are:

  • ES5 is no longer something that build tools or JavaScript libraries should target by default.

    If tools still want to offer ES5 support, it should be something individual sites with specific support needs can opt into.

  • Build tools and libraries should not use a fixed browser support policy.

    These policies can quickly become outdated, which leads to the scenario highlighted by the data in this article. Browser support decisions should be made by the site itself, not the tools it's using. A good browser support policy for tools and libraries is Baseline Widely Available.

  • Website developers who import third-party libraries should process those libraries as part of their build.

    It's not safe to assume that all library authors have the same browser support needs as you. And as the data in this article shows, in many cases website developers may have wider browser support needs than the libraries they're importing (and thus need to further transpile them).

  • Cross-browser support is not something that you should solely rely on your build tool to handle for you.

    If you need to support a specific set of browsers, then you need to be testing your site to ensure it works in those browsers.

Hopefully this article has been useful to anyone who still worries about ES5 support, or to anyone trying to convince others not to worry!

If you’re curious whether your site is serving script bundles that contain legacy ES5 helpers mixed in with untranspiled ES6+ code, you can search for your site in the data I shared above to see for yourself.

And if you discover that your site is doing this and you’re able to fix it based on recommendations from this article, I’d love to hear from you!