In the world of web development, optimization is key. Bootstrap is a popular choice for building responsive websites, but its default CSS and JS bundles often include a lot of unused code, resulting in larger bundle sizes. In this blog post, we'll guide you through the process of removing this unused CSS to create a more efficient and optimized production bundle using Bootstrap 5 and Webpack, with the help of PurgeCSS.
Before You Begin
To get started with this optimization process, ensure that you've set up your project according to the official Bootstrap + Webpack Getting Started guide. Follow the steps until you reach the "Extracting CSS" phase. Once done, your project structure should resemble the following Project Structure:
my-project/
├── src/
│ ├── js/
│ │ └── main.js
│ ├── scss/
│ │ └── styles.scss
│ └── index.html
├── package-lock.json
├── package.json
└── webpack.config.js
Adding an Example Page and noting the Base Metrics
To illustrate the optimization process, we'll use Bootstrap's official Pricing Example page. You can find the example code in a GitHub gist provided in this blog post. Update your index.html
and main.js
files according to the gist. After making these changes, run the following command:
npm run start
If everything is configured correctly, you should be able to view the Pricing Example page at http://localhost:8080. You can toggle the dark mode using the dropdown at the bottom right corner to verify that it functions as expected.

Noting Base Metrics
To understand the current state of your CSS bundle, run the following command:
npm run build
This command will provide information about the sizes of both the CSS and JS bundles, along with any associated warnings. For our purposes, we're primarily interested in the CSS bundle size, which should be around 227 KB.
> [email protected] build
> webpack build --mode=production
asset main.css 227 KiB [emitted] (name: main)
asset main.js 80 KiB [emitted] [minimized] (name: main) 1 related asset
asset index.html 13.6 KiB [emitted]
Entrypoint main [big] 307 KiB = main.css 227 KiB main.js 80 KiB
orphan modules 440 KiB (javascript) 975 bytes (runtime) [orphan] 87 modules
runtime modules 670 bytes 3 modules
cacheable modules 204 KiB (javascript) 227 KiB (css/mini-extract)
./src/js/main.js + 58 modules 204 KiB [built] [code generated]
css ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[0].use[2]!./node_modules/sass-loader/dist/cjs.js!./src/scss/styles.scss 227 KiB [built] [code generated]
WARNING in entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB). This can impact web performance.
Entrypoints:
main (307 KiB)
main.css
main.js
WARNING in webpack performance recommendations:
You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.
For more info visit https://webpack.js.org/guides/code-splitting/
webpack 5.89.0 compiled with 2 warnings in 4909 ms
Replicating a Production Environment
To obtain more realistic bundle size results, we'll set up a local Express.js server and compression. Begin by installing Express and Compression using the following command:
npm install express compression
Next, create an index.js file in your my-project directory and add the following code:
const express = require('express');
const compression = require('compression');
const app = express();
const port = 3000; // You can change this to any port you prefer
// Serve static files from the 'public' directory
app.use(compression());
app.use(express.static('dist'));
// Start the server
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
This code sets up an Express.js web server, serves static files from the 'dist' directory, and compresses the responses. It listens on port 3000, and a message will be displayed when the server starts.
Update your package.json file to include an "express" script:
{
// ...
"scripts": {
"start": "webpack serve",
"build": "webpack build --mode=production",
"express": "node index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
// ...
}
With everything set up, run the following command to build your project and start the Express server:
npm run build && npm run express
Visit the page at http://localhost:3000, right-click, select "inspect," and navigate to the network tab in the Inspector. Upon refreshing the page, you should see the main.css size, which should now be 31.5 KB.

Configure the PurgeCSS
PurgeCSS is a valuable tool for eliminating unused CSS from your project. We will integrate PurgeCSS into Webpack using the purgecss-webpack-plugin. Follow these steps:
Install the plugin with the following command:
npm i --save-dev purgecss-webpack-plugin
Next, update your webpack.config.js with the provided changes. Below are the necessary code modifications:
const path = require('path')
const glob = require("glob");
const autoprefixer = require('autoprefixer')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const miniCssExtractPlugin = require('mini-css-extract-plugin')
const {PurgeCSSPlugin} = require("purgecss-webpack-plugin");
const PATHS = {
src: path.join(__dirname, "src"),
};
module.exports = {
// ... (existing configuration)
plugins: [
// ... (existing plugins)
new miniCssExtractPlugin(),
new PurgeCSSPlugin({
paths: glob.sync(`${PATHS.src}/**/*`, {nodir: true}),
safelist: {
deep: [/dropdown-menu$/]
},
})
],
// ... (existing module rules)
}
Explanation of Changes:
- We've imported the glob library for efficient path handling.
- We've introduced the PurgeCSSPlugin and defined a PATHS object specifying the source directory.
- In the plugins section, we've added a new instance of PurgeCSSPlugin. This instance will search for CSS classes across the specified paths, and it includes a safelist rule for classes ending with "dropdown-menu."
You may need to customize the safelist according to your specific needs. For more details, check the plugin's documentation.
Finally, let's run the build again to get the final bundle size: npm run build && npm run express
. Then check the final bundle size in Inspector should be 5.8 KB.

Conclusion
In conclusion, optimizing your Bootstrap project by removing unused CSS can significantly reduce your bundle size. By following the steps outlined in this post, you can achieve a much smaller production bundle. Initially, the CSS bundle size was around 227 KB, but after implementing PurgeCSS and the necessary configurations, the final bundle size was reduced to a lean 5.8 KB.
This level of optimization can greatly enhance the performance of your web application, ensuring that only the necessary styles are included in your production code. For more details and access to the complete source code, please refer to the GitHub repository provided.