Performance

Some of Bootstrap 4's performance cost is fixable with better build habits. One piece of it isn't — it's baked into choosing Bootstrap 4 at all. Let's separate the two, and then apply the fixable ones to TrailMap.

The Unavoidable Cost: jQuery + Popper.js

Every interactive Bootstrap 4 component — modal, dropdown, collapse, carousel, tooltip, popover, scrollspy — is implemented as a jQuery plugin, and the positioning-heavy ones (tooltips, popovers, dropdowns) additionally depend on Popper.js. That means a real download-and-parse cost on every single page that loads Bootstrap 4's JS, whether or not that particular page actually uses a dropdown or a modal:

DependencyWhy Bootstrap 4 needs it
jQueryEvery component's JavaScript is written as a jQuery plugin ($.fn.modal, $.fn.collapse, etc.) — there's no jQuery-free code path
Popper.jsPowers the positioning math for dropdowns, tooltips, and popovers so they don't render off-screen
Bootstrap 4 vs Bootstrap 5: Bootstrap 5 rewrote every component in vanilla JavaScript and dropped the jQuery dependency entirely (Popper.js stayed, but only as an optional dependency for the specific components that need positioning). That's a genuine byte-for-byte, parse-time performance win Bootstrap 5 has that Bootstrap 4 structurally cannot — short of dropping Bootstrap 4's own JS and hand-rolling every interactive component yourself, there's no way around loading jQuery on a Bootstrap 4 page that uses them.

Fixable: Build Only the Sass Modules You Use

The CDN's bootstrap.min.css bundles every component's styles, whether or not the page uses them — carousel styles ship even to a page with no carousel. With the Sass workflow from Sass Customization, you control which component partials get imported at all:

// custom.scss — only pull in what TrailMap actually uses
@import "../node_modules/bootstrap/scss/functions";
@import "../node_modules/bootstrap/scss/variables";
@import "../node_modules/bootstrap/scss/mixins";
@import "../node_modules/bootstrap/scss/grid";
@import "../node_modules/bootstrap/scss/card";
@import "../node_modules/bootstrap/scss/navbar";
// no carousel, no toasts, no jumbotron partial — TrailMap doesn't use them

Skip the components TrailMap never uses (say, the carousel or toasts) and the compiled CSS is meaningfully smaller than the full CDN bundle, with zero visual difference on the pages that were never using those styles anyway.

Fixable: Minimize Custom CSS Overrides

Every additional stylesheet — especially one full of !important overrides fighting the compiled CSS, as covered in Sass Customization — is more bytes the browser has to download and more specificity conflicts it has to resolve. A project that overrides Sass $variables before compiling needs little to no override stylesheet at all; a project papering over the CDN build with !important rules accumulates more of them every time a new component needs the same treatment.

Worked Example: Lazy-Loading Images in TrailMap's Card Grid

TrailMap's trail card grid (from the Advanced Grid lesson) can easily have dozens of trail photos on one page. The native loading="lazy" attribute defers loading any image until it's about to scroll into view — no Bootstrap 4 class involved, just a plain HTML attribute on the <img>:

<div class="card">
  <img src="ridgeline-loop.jpg" class="card-img-top"
       loading="lazy" alt="Ridgeline Loop trail winding through pine forest">
  <div class="card-body">
    <h5 class="card-title">Ridgeline Loop</h5>
  </div>
</div>

Only the trail photos near the top of the page load immediately; the rest load as the visitor scrolls, which matters a lot on a page that might list fifty or more trail cards at once.

Next up, this track flagged plenty of Bootstrap-4-specific patterns along the way — pull them together into a real migration checklist in Migrating to Bootstrap 5.