Removing Unused CSS from Bootstrap 5 with PurgeCSS

Removing Unused CSS from Bootstrap 5 with PurgeCSS

Key Takeaways

  • A default Bootstrap 5 minified CSS build is roughly 157 KB. PurgeCSS can reduce that to under 20 KB on a focused landing page.
  • PurgeCSS works by scanning your HTML, JavaScript, and template files for class names, then stripping any CSS selector it never finds.
  • Bootstrap 5 uses dynamically added classes (via JavaScript) that PurgeCSS cannot see without a safelist. Missing these breaks interactivity.
  • A postcss.config.js integration is the most maintainable approach for HTML template projects in 2025 and 2026.
  • PurgeCSS should run only in production builds. Keep the full Bootstrap stylesheet in development for a smooth authoring experience.
  • Combining selective Sass imports with PurgeCSS gives the greatest file-size reduction of any purely CSS-based approach.

Why Bootstrap CSS File Size Matters in Practice

CSS is a render-blocking resource. The browser must download, parse, and apply your stylesheet before it can paint a single pixel of content above the fold. On a 4G mobile connection, every extra 10 KB of uncompressed CSS adds measurable latency. Google’s Core Web Vitals score weighs Largest Contentful Paint (LCP) heavily, and a bloated stylesheet is a direct contributor to a poor LCP time.

Gzip compression helps significantly. Bootstrap 5’s minified CSS compresses to roughly 23 KB over the wire, which is tolerable on its own. The problem compounds when you layer a premium HTML template on top. The Canvas HTML Template ships with its own component library, utility overrides, and plugin styles. A fully featured multi-demo build can produce a stylesheet exceeding 600 KB before compression. Stripping unused selectors brings the deliverable stylesheet for any single demo down to a fraction of that.

If you have read the post on building a custom Bootstrap 5 bundle by importing only the components you need, you already know that selective Sass imports are the first line of defence. PurgeCSS is the second line, catching anything the Sass approach misses.

Removing Unused CSS from Bootstrap 5 with PurgeCSS, abstract concept illustration

How PurgeCSS Works

PurgeCSS analyses a set of content files (your HTML pages, JavaScript files, template partials) and extracts every string that could plausibly be a CSS class name. It then compares that list against every selector in your stylesheet. Any selector containing no token found in the content files is removed from the output.

The default extractor uses a simple regex that matches any character sequence matching [^<>"'\s]*[^<>"'\s:]/. That covers most Bootstrap utility classes and custom component classes without any configuration. The extractor is pluggable, so you can write a custom one for frameworks with unusual class-naming conventions.

What PurgeCSS cannot see on its own:

  • Classes added by Bootstrap’s JavaScript at runtime (for example, show, collapsing, modal-open, dropdown-menu when opened)
  • Classes injected by third-party plugins like Swiper.js or GLightbox
  • Classes built from dynamic string concatenation in JavaScript
  • Selectors that target attribute values, pseudo-elements, or combinators that rely on dynamic state

These must be placed in a safelist, declared explicitly in your PurgeCSS configuration.

Installation and PostCSS Setup

The cleanest integration for an HTML template project is through PostCSS, which fits naturally into any build pipeline that already uses Autoprefixer or postcss-preset-env.

npm install --save-dev @fullhuman/postcss-purgecss

Create or update your postcss.config.js at the project root:

const purgecss = require('@fullhuman/postcss-purgecss');

module.exports = {
  plugins: [
    require('autoprefixer'),
    process.env.NODE_ENV === 'production' && purgecss({
      content: [
        './src/**/*.html',
        './src/**/*.js',
      ],
      safelist: {
        standard: [
          'show',
          'collapsing',
          'modal-open',
          'modal-backdrop',
          'offcanvas-backdrop',
          'dropdown-menu',
          'active',
          'disabled',
          'fade',
          'in',
        ],
        greedy: [
          /^swiper-/,
          /^glightbox/,
          /^data-/,
        ],
      },
      defaultExtractor: content =>
        content.match(/[\w-/:]+(?<!:)/g) || [],
    }),
  ].filter(Boolean),
};

The process.env.NODE_ENV === 'production' guard is critical. Running PurgeCSS during development removes classes before you have added all your HTML, causing confusing visual breakage and wasting build time.

remove unused css, abstract technical diagram

Building a Reliable Safelist for Bootstrap 5

Bootstrap 5 relies on JavaScript to add and remove classes at runtime for every interactive component. The safelist below covers the most commonly used components. Extend it based on the components your specific project uses.

safelist: {
  standard: [
    // Collapse and accordion
    'collapse',
    'collapsing',
    'collapsed',
    // Modal
    'modal-open',
    'modal-backdrop',
    'modal-dialog-scrollable',
    // Dropdown
    'dropdown-menu',
    'dropup',
    'dropend',
    'dropstart',
    // Visibility and transition helpers
    'show',
    'fade',
    'active',
    'disabled',
    // Toast and alert dismissal
    'showing',
    'hiding',
    // Offcanvas
    'offcanvas-backdrop',
    'offcanvas-open',
    // Tab panels
    'tab-pane',
  ],
  greedy: [
    /^bs-/,        // Bootstrap JS data attributes translated to classes
    /^tooltip/,    // Tooltip variants
    /^popover/,    // Popover variants
    /^carousel-/,  // Carousel state classes
  ],
},

Using greedy patterns on Bootstrap’s own namespace (/^bs-/) protects you against future Bootstrap patch releases that introduce new class names following the same convention.

Measuring the Reduction

After running a production build, compare file sizes with and without PurgeCSS. A simple shell script gives you an accurate picture:

gzip -k dist/css/bootstrap.min.css
ls -lh dist/css/bootstrap.min.css dist/css/bootstrap.min.css.gz

Typical results for a marketing landing page that uses the Bootstrap grid, navbar, modal, collapse, and standard utility classes:

  • Full Bootstrap 5.3 minified: 157 KB (23 KB gzipped)
  • After Sass selective imports: 90 KB (14 KB gzipped)
  • After PurgeCSS on top of selective imports: 16 KB (4 KB gzipped)

That final figure is an 80-plus percent reduction in over-the-wire CSS weight compared with shipping the full Bootstrap build. The impact on LCP on mobile connections is real and measurable in Chrome’s Lighthouse report.

This optimisation pairs well with the image delivery improvements described in the post on serving WebP and AVIF images from a static HTML template, giving you a comprehensive performance baseline for production.

When Not to Use PurgeCSS

PurgeCSS is not always the right tool. There are several situations where it creates more problems than it solves.

  • Content managed by a CMS or rich-text editor. If editors paste Bootstrap-classed markup into a WYSIWYG field, those classes will not appear in your static content files, and PurgeCSS will strip the corresponding CSS.
  • Dynamically rendered pages where class names are assembled at runtime. String concatenation like "col-" + size is invisible to the extractor. You need to safelist the full set of possible values.
  • Shared stylesheets served from a CDN for multiple sites. A single purged stylesheet built for Site A will likely break Site B, which may use different components.
  • Rapid prototyping phases. Adding PurgeCSS too early slows iteration. Lock it into the pipeline only when the component set is stable enough not to change every hour.

If your project involves heavy dynamic class generation, the selective Sass import approach described in customising Bootstrap 5 breakpoints and grid gutters with Sass will often deliver enough savings without the safelist maintenance burden.

Integrating PurgeCSS with a Premium HTML Template

When working with a multi-demo template like Canvas, the content glob must cover every HTML file in the demo you are deploying, including any partials or includes your build system assembles. A common mistake is pointing the content array only at index.html and missing dozens of classes used in section partials.

content: [
  './demo/**/*.html',   // All demo pages
  './src/js/**/*.js',   // Canvas and plugin JS that adds classes dynamically
  './src/scss/**/*.scss', // Sass files may contain class references in @extend or comments
],

Canvas uses a custom CSS variable system with the --cnvs-themecolor property and a set of utility classes that follow their own naming convention. Add a greedy pattern for the Canvas prefix to protect those:

greedy: [
  /^cnvs-/,
  /^swiper-/,
  /^glightbox/,
],

Run a full visual regression pass on the purged build before deploying. Check every interactive state: open modals, expanded accordions, active carousel slides, and visible tooltips. Any missing safelist entry will produce a broken visual state that is easy to spot but would be embarrassing to ship.

Frequently Asked Questions

Yes, but you must safelist the classes that Bootstrap’s JavaScript adds dynamically. Classes like show, collapsing, modal-open, and dropdown-menu are not present in your static HTML files, so PurgeCSS will remove their CSS unless you explicitly protect them in the safelist configuration.

Sass selective imports let you exclude entire Bootstrap components at the source level before any CSS is generated. PurgeCSS works on the compiled CSS output and removes individual selectors. The two approaches complement each other: Sass imports reduce the input, PurgeCSS cleans the output. Using both together gives the smallest possible stylesheet.

Only if those classes are not present in your content files. Utilities like d-md-flex or col-lg-6 will be preserved as long as they appear somewhere in your HTML or JavaScript source. The default extractor handles Bootstrap’s hyphenated class naming convention correctly without any extra configuration.

Add greedy safelist patterns that match the plugin’s class prefix. For Swiper, use /^swiper-/. For GLightbox, use /^glightbox/. Alternatively, include the plugin’s JavaScript source files in the PurgeCSS content array so the extractor can find the class names referenced inside the plugin code itself.

Results depend heavily on how many Bootstrap components your project uses. A focused landing page that uses only the grid, navbar, and modal can see a reduction from 157 KB down to 15 to 20 KB after purging. A complex multi-component application will retain more CSS. Gzip the output before measuring to understand real over-the-wire savings rather than raw file size.

Looking for a production-ready Bootstrap 5 HTML template? Browse Canvas Template demos and find the perfect starting point for your next project.

If you’re building with the Canvas HTML Template and want to ship production-ready Bootstrap 5 layouts faster, try Canvas Builder free — the visual builder that exports clean Canvas-ready markup in minutes.

Skip the setup and build it free

Spin up a complete Bootstrap 5 site, blog included, with Canvas Builder. No coding, no cost.

Share:
Canvas Team
Canvas Team

Tutorials and tips for building beautiful Bootstrap 5 websites with the Canvas HTML Template and Canvas Builder.

More from the Canvas Blog