The simplest way to use Bootstrap 4 is to add the whole thing from the CDNs.

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
    <title>Bootstrap Size Test</title>
</head>
<body>
    <div class="container">
        <div class="row">
            <div class="col-sm">
                page
            </div>
            <div class="col-sm">
                content
            </div>
            <div class="col-sm">
                goes
            </div>
            <div class="col-sm">
                here
            </div>
        </div>
    </div>
    <script src="//code.jquery.com/jquery-3.2.1.slim.min.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
    <script src="//maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script>
</body>
</html>

The problem is, you might not need everything Bootstrap has to offer. Adding the JavaScript like this adds 134.5 KB (ignoring gzipping for comparison below) to your page.

Full Bootstrap fom CDNs

If you are using ES2015+ and the npm distribution, and you don't need the whole Bootstrap package, you can import just the code you need. Here’s how.

Set up Bootstrap, Babel, and webpack

First, install Bootstrap, along with jQuery and Popper, which Bootstrap relies on. Then install webpack, along with tools to use ES2015+ syntax and minify JavaScript for distribution.

npm install bootstrap jquery popper.js
npm install --save-dev webpack babel-core babel-loader babel-preset-env clean-webpack-plugin exports-loader path uglifyjs-webpack-plugin

Next, update your webpack.config.js to resolve the /node_modules folder. You're setting this up so your import statements know where to look when you reference Bootstrap modules.

You'll also need to use exports-loader with the webpack ProvidePlugin to export the expected values from the Bootstrap modules and their dependencies. This helps with globals some of the Bootstrap modules depend on, like $ for jQuery.

Finally, I’ve added an alias for jQuery to point to jquery.slim.min.js since that’s enough jQuery functionality for Bootstrap to work. If you are already using the full jQuery in your project, skip this alias.

const webpack = require("webpack");
const path = require("path");
const CleanWebpackPlugin = require("clean-webpack-plugin");
const UglifyJsPlugin = require("uglifyjs-webpack-plugin");

module.exports = () => {
    return {
        entry: {
            index: "index.js"
        },
        output: {
            filename: "[name].js",
            path: path.resolve(__dirname, "dist"),
            publicPath: "/dist"
        },
        devtool: "source-map",
        plugins: [
            new CleanWebpackPlugin(["dist"]),
            new webpack.ProvidePlugin({
                $: "jquery",
                Popper: ["popper.js", "default"],
                Alert: "exports-loader?Alert!bootstrap/js/dist/alert",
                Button: "exports-loader?Button!bootstrap/js/dist/button",
                Carousel: "exports-loader?Carousel!bootstrap/js/dist/carousel",
                Collapse: "exports-loader?Collapse!bootstrap/js/dist/collapse",
                Dropdown: "exports-loader?Dropdown!bootstrap/js/dist/dropdown",
                Modal: "exports-loader?Modal!bootstrap/js/dist/modal",
                Popover: "exports-loader?Popover!bootstrap/js/dist/popover",
                Scrollspy: "exports-loader?Scrollspy!bootstrap/js/dist/scrollspy",
                Tab: "exports-loader?Tab!bootstrap/js/dist/tab",
                Tooltip: "exports-loader?Tooltip!bootstrap/js/dist/tooltip",
                Util: "exports-loader?Util!bootstrap/js/dist/util"
            }),
            new webpack.optimize.CommonsChunkPlugin({
                name: "common"
            }),
            new UglifyJsPlugin({
                sourceMap: true,
                uglifyOptions: {
                    dead_code: true
                }
            })
        ],
        resolve: {
            extensions: [
                ".js"
            ],
            modules: [
                path.resolve(__dirname, "scripts"),
                "node_modules"
            ],
            alias: {
                jquery: "jquery/dist/jquery.slim.min.js"
            }
        },
        module: {
            rules: [
                {
                    test: /\.js$/,
                    exclude: /node_modules/,
                    use: {
                        loader: "babel-loader",
                        options: {
                            presets: ["env"]
                        }
                    }
                }
            ]
        }
    };
};

Import just the needed modules

Now you can import the Bootstrap style sheet from /node_modules, assuming you want the same styling on every page.

<link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.min.css" />

You can also import Bootstrap styles from inside your webpack modules, but for most web apps, you’ll want the same styling on all pages, and putting a link to the style sheet in the <head> is an easy way to do that.

Now you can write code that imports only the needed Bootstrap modules like this.

import "bootstrap/js/dist/carousel";

Let's say have a page that uses the Bootstrap carousel. Here's the HTML, including a reference to a webpack-built index.js from my /dist folder that only includes the carousel reference above.

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.min.css" />
    <title>Bootstrap Size Test</title>
</head>
<body>
    <div class="container">
        <div id="carouselExampleIndicators" class="carousel slide" data-ride="carousel">
            <ol class="carousel-indicators">
                <li data-target="#carouselExampleIndicators" data-slide-to="0" class="active"></li>
                <li data-target="#carouselExampleIndicators" data-slide-to="1"></li>
                <li data-target="#carouselExampleIndicators" data-slide-to="2"></li>
            </ol>
            <div class="carousel-inner">
                <div class="carousel-item active">
                    <img class="d-block w-100" data-src="holder.js/800x400?auto=yes&amp;bg=777&amp;fg=555&amp;text=First slide" alt="First slide [800x400]" src="data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22400%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20800%20400%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_161c9dd15ef%20text%20%7B%20fill%3A%23555%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A40pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_161c9dd15ef%22%3E%3Crect%20width%3D%22800%22%20height%3D%22400%22%20fill%3D%22%23777%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22285.921875%22%20y%3D%22217.7%22%3EFirst%20slide%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E" data-holder-rendered="true">
                </div>
                <div class="carousel-item">
                    <img class="d-block w-100" data-src="holder.js/800x400?auto=yes&amp;bg=666&amp;fg=444&amp;text=Second slide" alt="Second slide [800x400]" src="data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22400%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20800%20400%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_161c9dd15f3%20text%20%7B%20fill%3A%23444%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A40pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_161c9dd15f3%22%3E%3Crect%20width%3D%22800%22%20height%3D%22400%22%20fill%3D%22%23666%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22247.3203125%22%20y%3D%22217.7%22%3ESecond%20slide%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E" data-holder-rendered="true">
                </div>
                <div class="carousel-item">
                    <img class="d-block w-100" data-src="holder.js/800x400?auto=yes&amp;bg=555&amp;fg=333&amp;text=Third slide" alt="Third slide [800x400]" src="data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22800%22%20height%3D%22400%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%20800%20400%22%20preserveAspectRatio%3D%22none%22%3E%3Cdefs%3E%3Cstyle%20type%3D%22text%2Fcss%22%3E%23holder_161c9dd15f8%20text%20%7B%20fill%3A%23333%3Bfont-weight%3Anormal%3Bfont-family%3AHelvetica%2C%20monospace%3Bfont-size%3A40pt%20%7D%20%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cg%20id%3D%22holder_161c9dd15f8%22%3E%3Crect%20width%3D%22800%22%20height%3D%22400%22%20fill%3D%22%23555%22%3E%3C%2Frect%3E%3Cg%3E%3Ctext%20x%3D%22277%22%20y%3D%22217.7%22%3EThird%20slide%3C%2Ftext%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E" data-holder-rendered="true">
                </div>
            </div>
            <a class="carousel-control-prev" href="#carouselExampleIndicators" role="button" data-slide="prev">
                <span class="carousel-control-prev-icon" aria-hidden="true"></span>
                <span class="sr-only">Previous</span>
            </a>
            <a class="carousel-control-next" href="#carouselExampleIndicators" role="button" data-slide="next">
                <span class="carousel-control-next-icon" aria-hidden="true"></span>
                <span class="sr-only">Next</span>
            </a>
        </div>
    </div>
    <script src="/dist/common.js"></script>
    <script src="/dist/index.js"></script>
</body>
</html>

The page now uses the same full Bootstrap CSS, which accounts for most of the Bootstrap weight at 141 KB. But the size of the JavaScript is just over 76 KB, saving us about 58 KB from the original approach.

Tree shaken Bootstrap

It is much simpler to pull in the full Bootstrap library, but if you need to squeeze out a little more performance or shrink your page weight a little more, pulling in only the Bootstrap libraries you need can help.

The sample code for this post is available on GitHub.