Using ES5 on the Web

Ecmascrpit standard

Ecmascrpit standard

In 2017 I wrote articlewhich showed web developers how they could deploy ES6+ (aka ES2015) code to production without having to transpile it to ES5. This method was a go-to for web developers who wanted to be free to write modern code without worrying about transpiler or polyfill bloat.

Unfortunately, while many web developers could use this method, most JavaScript library authors could not.

Library authors face far more restrictions than web developers, as they have no control over how their code is deployed. Additionally, since many of the popular build tools recommend that developers exclude the node_modules from transpilations, library authors were forced to be extremely conservative – usually transpiling everything back to ES5 to avoid potentially breaking sites.

But that was seven years ago, and there have been many improvements in JavaScript tooling since then. The browser landscape has also changed dramatically. In particular, IE 11, the last remaining ES5 browser, will no longer be supported by Microsoft in 2022which meant that many companies might finally stop supporting it.

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

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 web developers and library authors should approach supporting legacy browsers in the future.

A small disclaimer

Before I dive into the actual data about ES5 usage, I want to make it clear that there is nothing inherently wrong with creating or publishing ES5 code.

JavaScript engines take much longer to optimize ES5 code than modern code, so if you have old ES5 code that still works, there's no point in updating it just to make it “modern.”

However, if you create code in ES6+ syntax and then use a build tool to transpile it to ES5, this will typically result in a lot of polyfills and transpilers, which can significantly increase the size of your final bundles.

To illustrate this point, let us give an example:

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

If you manually translate this code to ES5, 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 using Babel And configure it to add polyfills – even if you limited it to only the necessary polyfills based on the usage in the source code – it includes 71 core-js dependencies and decreases from 31 bytes to 11,217 bytes minified see!

The point of this example is not to shame Babel or core-js. These tools must support all possible ES6+ code, which requires them to account for all sorts of edge cases (though this particular example does not have any).

Instead, the goal is to highlight that choosing to support legacy browsers comes at 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 actually transpile and deploy their code to production, then it turns out that most sites on the Internet deliver code transpiled to ES5, but still does not work in IE 11. This means that the transpiler and bloated polyfill are downloaded by 100% of their users, but provide no benefit to any of them.

Data

To understand the state of ES5 on the web, you need to consider three things, as they all play an important role in the final form of our code that we, as web users, receive:

  • Default configurations of popular builders and tools

  • Status of code found in popular JavaScript libraries

  • Status of code deployed by website owners

Default settings of collectors

Most builders and tools are very flexible and offer almost unlimited control over the final result. However, in practice, most developers simply use the default values, so default values ​​are important.

What are these defaults? In particular, do defaults cause code to be transpiled to ES5?

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

Tool

Transpiles to ES5 by default?

Comment

Browserlist

No

It is not a build tool, but is used internally by many build tools and is the most popular open source tool for targeting support browsers. Default setting no longer includes ES5 browsers. The last one was IE 11, which was marked dead in version 4.21.

Babel

Yes

The Babel documentation recommends setting the parameter target (which uses Browserlist), but if nothing is specified, all code will be transpiled to ES5.

Webpack

No

By default, webpack does not transpile any code. Most webpack users include babel-loaderand the webpack example for this suggests setting targets: “defaults”.

Typescript (tsc)

Yes

By default in TypeScript config target – ES5.

Next.js

No

Next.js uses Babel for transpilation and by default sets the Browserlist configuration to “modern browsers» (i.e. browsers that support ES modules).

esbuild

No

esbuild does not transpile by defaultYou can specify a custom target to enable transpilation, but ES5 is not supported as a transpilation target.

Vite

No

Vite uses esbuild and by default sets a custom target for “modern browsers» (i.e. browsers that support ES modules). Vite allows users to install pluginif they need support for older browsers.

Rollup

No

Rollup is not transpiled by default. Many Rollup users install @rollup/plugin-babelin this case the Babel default values ​​are used.

Parcel

No

Parcel automatically applies differential feed with customizable goals.

Closure Compiler

No

By default ECMASCRIPT_NEXT is used, which is the latest set of stable ES functions.

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

However, Babel is still the most popular tool for transpiling JavaScript, and as a result, transpiling to ES5 is still quite common on the web (see below). using ES5 in production for more detailed information).

Popular Javascript Libraries

In addition to reviewing popular build tools, I also looked at some of the most popular libraries used today (again based on a survey State of JSin rough order of popularity):

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

Here's what I found:

Library

Contains ES6+ syntax?

Comments (left original terms)

Lodash

No

ES5 only

React

No

ES5 only

date-fns

Yes

arrow functions

three.js

Yes

async/await, arrow functions, spread, destructuring

d3

Yes

arrow functions, spread, destructuring

Framer-motion

Yes

arrow functions, spread, destructuring

greensock

No

ES5 only

dayjs

No

ES5 only

Zod

Yes

async/await, arrow functions, spread, destructuring

RxJS

Yes

arrow functions

immer

Yes

arrow functions, spread, destructuring

Luxxon

Yes

async/await, arrow functions, spread, destructuring

react-query

No

ES5 only

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

This is notable because, as I mentioned earlier, most developers who use Babel to transpile their source files when building explicitly configure their builder not to transpile anything in the directory node_modules — which has been the main reason why library authors have historically felt they needed to continue transpiling to ES5.

For example, at the time of publication of this article (September 2024):

A TypeScript (tsc), the second most popular transpilation tool after Babel, will only transpile a project's own code files. It will not transpile the project's dependencies into node_modules.

This creates a problem for any website that wants to support ES5 and uses Babel or tsc to transpile their code. Unless they have a deep understanding of how all the parts of their build pipeline interact with each other, and unless they know how to properly configure each of them, they are likely to be merging ES6+ code into their ES5 code without realizing it.

So the question is, does this really cause a problem for real websites or do most of them set up their tools correctly? The next section looks at data from HTTP archiveto answer this question.

Note: Some libraries in the table above publish both ES5 and ES6+ versions, usually with the ES5 version set in package.main and the ES6+ version set in either package.module or package.exports. In these cases, I only looked at the script version the bundler got when using the default configuration (since that's what most people use), and bundlers today default to using package.module or package.exports instead of package.main (see: [1], [2], [3]).

Using ES5 in production

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

All three of these tools include some form of polyfills and so-called ES5 “helper” functions to avoid duplication in the 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 you can detect (even in minified code) which sites are using them by requesting an HTTP archive. Looking for the presence of these helper functions — rather than standard ES5 syntax (like var or non-arrow functions) — helps distinguish old hand-written ES5 code (usually quite optimized) from new ES5 code generated by a transpiler (usually quite bloated).

I spent search on the HTTP Archive to see how common it is for popular websites (the top 10,000, according to popularity rating CrUX). I also wanted to know how common it is for sites to use untranspiled ES6+ syntax.

Here's what I found (full results):

  • 89% sites serve at least 1 JavaScript file containing untranspiled ES6+ syntax.

  • 79% sites serve at least 1 JavaScript file containing ES5 helper code.

  • 68% sites serve at least 1 JavaScript file that contains both ES5 helper code and untranspiled ES6+ syntax in a single file.

The latest discovery just blew my mind.

To repeat what I said earlier — because it bears repeating — if a browser doesn't support ES6+ syntax (like IE 11), it will throw an error when it tries to load a script file that contains ES6+ syntax. And if a browser does support ES6+ syntax, it doesn't need any ES5 helper code or legacy polyfills. There's absolutely no reason to include both.

To further verify the accuracy of this query's results, I manually tested 20 random sites from the list and confirmed that they do indeed include both ES5 helper code and ES6+ syntax in some of the same script bundles. I also manually visited these sites in IE 11 and confirmed that these script bundles are indeed not loading.

Remember, these aren't just random websites on the internet. These are the 10,000 most popular websites in the world, which account for the vast majority of all website usage in the world.

What does this all mean?

For a site that provides users with code containing both ES5 helpers and untranspiled ES6+ syntax, there are only two plausible explanations:

  • The site doesn't have to support ES5 browsers, but some of their dependencies are transpiled to ES5, so their output shows ES5 code.

  • The site planned to support ES5 browsers, but the developers failed to take into account that some of their dependencies publish untranspiled ES6+ syntax, and did not configure their builder to transpile the code to node_modules.

Regardless of the explanation, the fact that so many of the world's most popular websites are emitting so much unnecessary code is a strong indicator that the default settings our tools currently recommend aren't working.

If there's one good thing about this data, it's that it's pretty clear to me that IE's deprecation won't have a noticeable impact on most companies. If all these big companies are apparently unaffected by these broken IE features, then yours probably won't be either.

Recommendations

For library authors

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 now ship some untranspiled ES6+ syntax, that rationale is no longer valid.

Given the data presented in this article, it definitely makes no sense for JavaScript library authors to transpile their code to ES5.

From a practical standpoint, library authors have no information about the browser support needs of websites that import them, so it makes no sense for them to make this decision for all consumers of their library. At the same time, library authors should not assume that all consumers of their library will be able to run it through a complex build process, so it is important that their published code uses fully standard JavaScript and works in the current set of widely used browsers.

So what goals should library authors choose? In my opinion, the best solution for library authors is to use Baselinenamely the inclusion of only functions Baseline Widely Available into any published code.

If you're not familiar with Baseline, it's a community group effort WebDX at W3C to help developers easily identify features that are stable and well supported by all major browsers and browser rendering engines on desktop and mobile devices. A feature is considered Baseline Widely Availableif it has been available in stable versions of all four major browsers for at least 30 months.

The main advantage 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 ES5 targeting (and what's currently happening with the esmodule target used by Next.js, Vite, and Parcel).

Library authors can now configure their build system to use the underlying widely available functionality with the following request Browserlist (for any tool that supports Browserlist):

targets: [
  'chrome >0 and last 2.5 years',
  'edge >0 and last 2.5 years',
  'safari >0 and last 2.5 years',
  'firefox >0 and last 2.5 years',
  'and_chr >0 and last 2.5 years',
  'and_ff >0 and last 2.5 years',
  'ios >0 and last 2.5 years',
]

Note: There is a request open to add Baseline support to Browserlist, which would simplify the above request to simply “baseline is widely available”.

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

For Web Developers

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

I argued that this was bad practice. since 2017but most developers I talked to were reluctant to follow this advice because it would slow down their builds.

However, build tools are much faster these days. Additionally, sites can configure their builds to only process code in node_modules when building for production. During development, the code should work fine in any browser the developer uses, especially if library authors follow the advice I gave above and target Baseline Widely Available.

Conclusions

As I mentioned above, the purpose of this article is not to blame or shame websites or tool authors based on these results. I also want to make it clear that I am definitely not suggesting that websites should still support IE 11. If anything, these results suggest that supporting IE 11 is not a necessity for most companies, even large ones with a global customer base.

The key takeaways I want readers to take away from this article are:

  • ES5 is no longer something that JavaScript build tools or libraries should target by default.
    If tools still want to offer ES5 support, it should be something that individual sites with specific support needs can choose to do.

  • Build tools and libraries should not have a fixed browser support policy.
    These policies can quickly become out of date, leading to the scenario described in the data in this article. Decisions about browser support should be made by the site itself, not by the tools it uses. A good browser support policy for tools and libraries is Baseline Widely Available.

  • Website developers who import third-party libraries must handle those libraries as part of their build.
    Don't 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 broader browser support needs than the libraries they import (and therefore need to transpile them additionally).

  • Cross-browser support is not something you should rely solely on when building your tool.
    If you need to support a specific set of browsers, you need to test your site to make sure it works in those browsers.


I hope this article was helpful to anyone still worried about ES5 support, or anyone trying to convince others not to worry!

If you're wondering if your site is serving script bundles that contain legacy ES5 helpers mixed in with untranspiled ES6+ code, you can search your site in datawhich I shared above so you can see for yourself.

And if you find that your site does this and can fix it based on the recommendations in this article, I'll be glad hear from you!

If you liked the article, more similar material and news from the world of frontend development in my telegram channel Smooth as css animation

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *