Skip to content

How to optimize your PageBuilder Engine bundle size for better page load and render performance

This document explains a few key concepts around what affects your final JavaScript bundle size and browser rendering performance. We cover ways to understand, optimize and measure page performance.

Understand and analyze why your bundle is getting bigger

When creating new features, you may want to use popular frameworks like material UI or common utility libraries like lodash or moment.js. PageBuilder Engine utilizes webpack and babel with custom plugins to package your features to run in Arc infrastructure at scale. Webpack and babel streamline the dependencies to be easily bundled into a final JavaScript package. It’s easy to lose track of what’s included in your final application bundle file when including a library without fully realizing its impact on the final JavaScript bundle size. This not only affects the network transfer speed but also JavaScript execution time in the browser that sums up to page load and rendering speed.

There are a few ways that will help you understand what goes into your bundle code and increase your awareness when picking libraries to use and their effect in the final js bundle size. You will:

  • Realize what’s really inside your bundle

  • Find out the modules make up the most of its size

  • Find modules that got there by mistake

  • Consider alternatives to optimize your bundle size

There are a few popular tools we can use with minimal effort to analyze our bundle visually.

Webpack bundle analyzer

Install:

Terminal window
npm install --save-dev webpack-bundle-analyzer

Run engine locally to generate stats.json file. See the Using locally generated Webpack stats file to learn how to get the stats.json file from your bundle code. When creating the stats.json file locally, by default your webpack will not apply minification. See the note in the linked document about enabling minification in local builds).

Start bundle analyzer:

Terminal window
npx webpack-bundle-analyzer .fusion/dist/stats.json .fusion/dist/components/combinations/

The bundle analyzer will start as a web application in its default port. Visit http://127.0.0.1:8888/ to open bundle analyzer web UI. You can analyze your bundle and the packages/dependencies you use and determine the costly dependencies and think about strategies to optimize or find lightweight alternatives using the bundle analyzer:

Bundle analyzer

Webpack Visualizer

Webpack visualizer is another tool we can use to create a visual representation of your final bundle. Using the webpack visualizer is very easy. To analyze your bundle content:

  1. First, Run the engine locally to generate stats.json file. See the Using locally generated Webpack stats file to learn how to get stats.json from your bundle code.

  2. Use their Webpack Visualizer web tool to upload your stats.json file.

Webpack visualizer

Bundlephobia

Aside from analyzing your existing bundle contents, you can also proactively be conscious of every dependency before adding to your bundle. Bundlephobia helps you to search packages and know what you are getting into, the cost of the packages in your application. Bundlephobia also highlights the user/browser impact of this package when used in your application, like download and render timings. There is also a historical change between versions of the package you are analyzing highlighted in bundlephobia:

Bundlephobia

Install, import and bundle only what you need using Path Imports

Large libraries or SDKs often come with many internal modules. Without understanding what we really utilize from these libraries, we often import the full library in our code and only use a few methods. It’s common in popular frameworks (i.e Material UI) that comes with modular code that we can only import what we want to use. This allows webpack to only include the code used from these libraries without including everything from the library. Analyzing your bundle contents with the methods mentioned above will give you the first step to realize what’s taking large space in your bundles. From there, you can determine to further optimize your imports from the libraries you use for only what you really need.

When you traditionally import exported content from the libraries you utilize, webpack and babel import the full library, this both the transpile speed slower as well as causes the full library to be included in the final bundle. A common optimization practice called path imports helps you to explicitly define the methods or imports you really need in your imports that will reduce your bundle size. Keep in mind that not all npm packages have path imports.

Instead of the top-level import:

import { Button } from '@material-ui/core';

Import from paths:

import Button from '@material-ui/core/Button';

You must be consistent in your import conventions throughout your codebase. This means if you are path importing the components from the libraries you use, you have to make sure all import from these libraries follows the same path imports. Having inconsistent imports, where you leave a few places you still import with top-level will still result in webpack bundling the whole library contents in your final bundle. You can tweak your imports and re-run the bundle analyzer to check if your library’s presence in your final bundle gets smaller and gets optimized.

This optimization is helpful not only for the final bundle size for your visitor’s page load and render speed, but also will help you to speed up the PageBuilder Engine’s build and rebuild time since webpack and babel exactly knows what to look for in your libraries rather than keep importing everything your library includes.

Avoid using heavy libraries

Libraries like lodash or moment.js are powerful libraries but they come with a cost that we covered above. As we analyze the real cost of these libraries, we can get more selective and careful of selecting these libraries. There are other packages that replace these popular libraries with the same/similar APIs that are very easy to migrate to that costs much less. A good example of this is moment.js that we already touched in our PageBuilder Engine best practices documentation.

Libraries

Opt-in to use “static” rendering as much as possible

PageBuilder Engine comes with a powerful feature called static. Static features are only rendered on the server-side and don’t add any of the libraries used to the front-end bundle. Therefore once it’s rendered on the server-side, your dependencies don’t cause an extra load to the user’s network traffic since these dependencies never get to the front-end bundle if they are not used in the front-end code. Static is applicable to any feature that doesn’t have interactivity and client-side JavaScript to be executed in it.

To make a feature to be static and only server-side rendered, add .static prop to your feature component.

See the Static content component for more information about static components.

Use PageBuilder Engine provided code splitting

One of the side effects of packaging all JavaScript code into bundles in complex web applications is the large bundle sizes in larger-scale apps. As you add more feature packs to your bundles, your bundle size and the amount of code included in your bundles will get bigger. Code splitting is a concept that splits your final bundle into smaller pieces (chunks) and loads only pieces that are needed to render your page. This concept is a good performance optimization when done right. Creating too many bundle “chunks” is not ideal either.

PageBuilder Engine comes with code splitting (starting with 3.0 release) out of the box. Code splitting in PageBuilder Engine is not enabled by default and you need to enable it per feature. We highly encourage you to enable code splitting in large dependency utilized features. This ensures your heavy feature can be isolated and saves its load in the final bundle on the pages that don’t use this feature. See the Code Splitting Documentation to learn more.

We have seen promising improvement on a few key performance metrics we measured from the tests we ran with code splitting:

Performance metrics

Serve images in your features the right way

It’s important to make sure the right resolution with proper optimizations is applied when rendering image content. Images hosted in Photo Center come with an image resizer with various transformations and modern compression and encoding mechanism. Arc recommends that all images be properly resized and compressed using Resizer V2.

Cache and CDN

One of the prominent benefits of relying on Arc infrastructure is to ensure your page can serve large-scale traffic. Arc utilizes sophisticated caching approaches to your content sources, rendered pages. To learn more about how caching works in Arc, you can See the the following references:

To ensure PageBuilder Engine utilizes a properly cached content source from your application regardless of server-side rendering or the client-side, we suggest you make all of your content calls through content sources. This applies to API calls you make to both Arc content APIs and external API calls. Arc tries to cache all content calls from your application allowing Arc to be able to handle large traffic cost-effectively and serve cached content through our CDN network.

Measuring and monitoring your page performance

We compiled our recommended method of testing your page performance and a few key points to be aware of when running your performance tests. See Measuring and monitoring your page performance