Why Your Production Bundles Are Huge!
Learn common causes of large production bundles. To demonstrate, we will bundle a React application for production using Webpack.
Table of Contents 📖
Large Bundle
Below is the output of a React bundle for a small application. Notice how the bundle.js file is 1.6 MB in size. This is very large for such a small application.
npm run build
-rw-r--r-- 1 wittcode staff 1622657 Sep 3 11:27 bundle.js
-rw-r--r-- 1 wittcode staff 971 Sep 3 11:27 bundle.js.LICENSE.txt
-rw-r--r-- 1 wittcode staff 244 Sep 3 11:27 index.html
There are three main reasons for bundles this large: assets, dependencies, and environment variable settings.
Handling Assets
First lets look at the assets. This application consists of images, all of which are being included in the bundle. Check out the configuration below.
{
test: /\.(png|svg|jpg|jpeg|gif)$/,
type: 'asset/inline'
}
This configuration tells webpack to inline images. This will include them in the bundle. Instead, we can make separate calls for the images. We can do this by setting the type to asset/resource.
{
test: /\.(png|svg|jpg|jpeg|gif)$/,
type: 'asset/resource'
}
npm run build
-rw-r--r-- 1 wittcode staff 146705 Sep 3 11:34 bundle.js
-rw-r--r-- 1 wittcode staff 971 Sep 3 11:34 bundle.js.LICENSE.txt
drwxr-xr-x 6 wittcode staff 192 Sep 3 11:34 images
-rw-r--r-- 1 wittcode staff 244 Sep 3 11:34 index.html
Change the type to asset/resource causes Webpack to emit the images as files in the bundle and export their URL. Notice how the bundle size is now 146.705 kilobytes.
Handling Dependencies
The other cause for large bundles is dependencies. When bundling, Webpack will ignore a package if it has not been imported and used. For example, installing React MUI will keep the bundle size the same.
npm i @mui/material @emotion/react @emotion/styled
ERROR: React MUI requires Emotion dependencies.
npm run build
-rw-r--r-- 1 wittcode staff 146705 Sep 3 12:17 bundle.js
-rw-r--r-- 1 wittcode staff 971 Sep 3 12:17 bundle.js.LICENSE.txt
drwxr-xr-x 6 wittcode staff 192 Sep 3 12:17 images
-rw-r--r-- 1 wittcode staff 244 Sep 3 12:17 index.html
However, as soon as we import something from the library and use it, it will be added to the bundle.
import {createRoot} from 'react-dom/client';
import './assets/styles/globals.css';
import App from './components/App';
import Stack from '@mui/material/Stack';
const root = createRoot(document.getElementById('root'));
root.render(<Stack><App /></Stack>);
npm run build
-rw-r--r-- 1 wittcode staff 215163 Sep 3 12:18 bundle.js
-rw-r--r-- 1 wittcode staff 1154 Sep 3 12:18 bundle.js.LICENSE.txt
drwxr-xr-x 6 wittcode staff 192 Sep 3 12:18 images
-rw-r--r-- 1 wittcode staff 244 Sep 3 12:18 index.html
This is why it is important to install packages that are necessary. Large packages can bloat the bundle tremendously. Module bundlers also generally come with a way to exclude certain modules from the bundle. For example, we could exclude the packages React and React DOM from the bundle with Webpack.
externals: {
react: 'react',
'react-dom': 'react-dom'
}
npm run build
-rw-r--r-- 1 wittcode staff 75583 Sep 3 12:22 bundle.js
-rw-r--r-- 1 wittcode staff 432 Sep 3 12:22 bundle.js.LICENSE.txt
drwxr-xr-x 6 wittcode staff 192 Sep 3 12:22 images
-rw-r--r-- 1 wittcode staff 244 Sep 3 12:22 index.html
Environment
The final common cause is environment variables. Specifically, not setting the environment as production. For example, in Node the NODE_ENV variable determines the type of environment. Setting it to production enables certain features that are only available in production. One of these features for Webpack is code minification.
mode: 'development'
npm run build
-rw-r--r-- 1 wittcode staff 545160 Sep 3 12:25 bundle.js
drwxr-xr-x 6 wittcode staff 192 Sep 3 12:25 images
-rw-r--r-- 1 wittcode staff 257 Sep 3 12:25 index.html
Here, setting the mode to development creates a much larger bundle.