
In this codelab, improve the performance of this simple application that allows users to rate random cats. Learn how to optimize the JavaScript bundle by minizming how much code is transpiled.

In the sample app, you can select a word or emoji to convey how much you like each cat. When you click a button, the app displays the button's value underneath the current cat image.
Measure
It's always a good idea to begin by inspecting a website before adding any optimizations:
- To preview the site, press View App. Then press
Fullscreen
.
- Press `Control+Shift+J` (or `Command+Option+J` on Mac) to open DevTools.
- Click the Network tab.
- Select the Disable cache checkbox.
- Reload the app.

Over 80 KB is used for this application! Time to find out if parts of the bundle aren't being used:
Press
Control+Shift+P(orCommand+Shift+Pon Mac) to open the Command menu.
Enter
Show Coverageand hitEnterto display the Coverage tab.In the Coverage tab, click Reload to reload the application while capturing coverage.

Take a look at how much code was used versus how much was loaded for the main bundle:
Over half the bundle (44 KB) is not even utilized. This is because a lot of the code within consists of polyfills to ensure that the application works in older browsers.
Use @babel/preset-env
The syntax of the JavaScript language conforms to a standard known as ECMAScript, or ECMA-262. Newer versions of the specification are released every year and include new features that have passed the proposal process. Each major browser is always at a different stage of supporting these features.
The following ES2015 features are used in the application:
The following ES2017 feature is used as well:
Feel free to dive into the source code in src/index.js to see how all of this
is used.
All of these features are supported in the latest version of Chrome, but what about other browsers that don't support them? Babel, which is included in the application, is the most popular library used to compile code that contains newer syntax into code that older browsers and environments can understand. It does this in two ways:
- Polyfills are included to emulate newer ES2015+ functions so that their APIs
can be used even if it is not supported by the browser. Here is an example of a
polyfill
of the
Array.includesmethod. - Plugins are used to transform ES2015 code (or later) into older ES5 syntax. Since these are syntax related changes (such as arrow functions), they cannot be emulated with polyfills.
Look at package.json to see which Babel libraries are included:
"dependencies": {
"@babel/polyfill": "^7.0.0"
},
"devDependencies": {
//...
"babel-loader": "^8.0.2",
"@babel/core": "^7.1.0",
"@babel/preset-env": "^7.1.0",
//...
}
@babel/coreis the core Babel compiler. With this, all the Babel configurations are defined in a.babelrcat the root of the project.babel-loaderincludes Babel in the webpack build process.
Now look at webpack.config.js to see how babel-loader is included as a
rule:
module: {
rules: [
//...
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
}
]
},
@babel/polyfillprovides all the necessary polyfills for any newer ECMAScript features so that they can work in environments that do not support them. It is already imported at the very top ofsrc/index.js.
import "./style.css";
import "@babel/polyfill";
@babel/preset-envidentifies which transforms and polyfills are necessary for any browsers or environments chosen as targets.
Take a look at the Babel configurations file, .babelrc, to see how it's
included:
{
"presets": [
[
"@babel/preset-env",
{
"targets": "last 2 versions"
}
]
]
}
This is a Babel and webpack setup. Learn how to include Babel in your application if you happen to use a different module bundler than webpack.
The targets attribute
in .babelrc identifies which browsers are being targeted. @babel/preset-env
integrates with browserslist, which means you can find a full list of compatible
queries that can be used in this field in the
browserlist documentation.
The "last 2 versions" value transpiles the code in the application for the
last two versions of every browser.
Debugging
To get a complete look at all the browser's Babel targets as well as all the
transforms and polyfills that are included, add a debug field to .babelrc:
{
"presets": [
[
"@babel/preset-env",
{
"targets": "last 2 versions",
"debug": true
}
]
]
}
- Click Tools.
- Click Logs.
Reload the application and take a look at the Glitch status logs at the bottom of the editor.
Targeted browsers
Babel logs a number of details to the console about the compilation process, including all the target environments that the code has been compiled for.

Notice how discontinued browsers, such as Internet Explorer, are included in this list. This is a problem because unsupported browsers won't have newer features added, and Babel continues to transpile specific syntax for them. This unnecessarily increases the size of your bundle if users are not using this browser to access your site.
Babel also logs a list of transform plugins used:

That's a pretty long list! These are all the plugins that Babel needs to use to transform any ES2015+ syntax to older syntax for all the targeted browsers.
However, Babel doesn't show any specific polyfills that are used:
![]()
This is because the entire @babel/polyfill is being imported directly.
Load polyfills individually
By default, Babel includes every polyfill needed for a complete ES2015+ environment when
@babel/polyfill is imported into a file. To import specific polyfills needed for
the target browsers, add a useBuiltIns: 'entry' to the configuration.
{
"presets": [
[
"@babel/preset-env",
{
"targets": "last 2 versions",
"debug": true
"useBuiltIns": "entry"
}
]
]
}
Reload the application. You can now see all the specific polyfills included:

Although only needed polyfills for "last 2 versions" is now included, it is still a super long list! This is because
polyfills needed for the target browsers for every newer feature is still included. Change the value of the attribute to usage
to only include those needed for features that are being used in the code.
{
"presets": [
[
"@babel/preset-env",
{
"targets": "last 2 versions",
"debug": true,
"useBuiltIns": "entry"
"useBuiltIns": "usage"
}
]
]
}
With this, polyfills are automatically included where needed.
This means you can remove the @babel/polyfill import in src/index.js.
import "./style.css";
import "@babel/polyfill";
Now only the required polyfills needed for the application are included.

The application bundle size is reduced significantly.

Narrowing the list of supported browsers
The number of browser targets included is still quite large, and not many users use discontinued browsers such as Internet Explorer. Update the configurations to the following:
{
"presets": [
[
"@babel/preset-env",
{
"targets": "last 2 versions",
"targets": [">0.25%", "not ie 11"],
"debug": true,
"useBuiltIns": "usage",
}
]
]
}
Take a look at the details for the fetched bundle.

Since the application is so small, there really isn't much of a difference with
these changes. However, using a browser market share percentage (such as
">0.25%") along with excluding specific browsers that you are confident your
users are not using is the recommended approach. Take a look at the
"Last 2 versions" considered harmful
article by James Kyle to learn more about this.