Customizing Bootstrap 5 with Sass Variables
This guide shows you how to customize Bootstrap 5 using Sass variables. You'll learn how to modify colors, typography, spacing, and components…
This guide breaks down the essential changes in components, grid system, utilities, and JavaScript functionality, providing practical examples and migration strategies.
One of the most significant changes in Bootstrap 5 is that it no longer requires jQuery as a dependency. Bootstrap 4 (and earlier versions) depended on jQuery to power its JavaScript plugins (like modals, dropdowns, tooltips, etc.), which meant including the jQuery library for Bootstrap’s interactive components to work. In Bootstrap 5, all plugins have been rewritten in plain “vanilla” JavaScript This removal of jQuery leads to a lighter bundle and eliminates a dependency that many modern JavaScript frameworks were avoiding. In fact, the Bootstrap team highlights this as the biggest change in the upgrade Now, Bootstrap’s own JS is framework-agnostic and doesn’t force developers to include jQuery if they don’t need it for other reasons.
From a practical perspective, dropping jQuery impacts how you initialize and use Bootstrap components in JavaScript. In Bootstrap 4, you might have used patterns like $('#myModal').modal('show') or $('[data-toggle="tooltip"]').tooltip(). In Bootstrap 5, these are replaced with a new JavaScript API that can be used directly. For example, to show a modal, you would do: new bootstrap.Modal(document.getElementById('myModal')).show(). All Bootstrap 5 plugins can be initialized either by selecting the element (via document.querySelector or passing an ID as a string) or automatically via data attributes The example below demonstrates the difference:
// Bootstrap 4 (with jQuery)
$('#myModal').modal('show');
// Bootstrap 5 (without jQuery)
var myModal = new bootstrap.Modal(document.getElementById('myModal'));
myModal.show();
In Bootstrap 5, if jQuery is present on the page, it will still work seamlessly with Bootstrap’s plugins (you can even use the jQuery interface as an optional convenience), but it’s not required This change makes Bootstrap 5 more lightweight and compatible with modern build systems and frameworks (like React or Angular) that might not include jQuery by default.
Another related change is the introduction of namespaced data attributes. In Bootstrap 4, to activate things like modals or collapsible elements via HTML, you would use data-toggle="modal" or data-toggle="collapse". In Bootstrap 5, these attributes are now data-bs-toggle="modal", data-bs-toggle="collapse", etc., to avoid conflicts and make it clearer that they are part of Bootstrap’s JS For example, a toggle button for a dropdown would be written as:
<!-- Bootstrap 4: -->
<button class="dropdown-toggle" data-toggle="dropdown">Menu</button>
<!-- Bootstrap 5: -->
<button class="dropdown-toggle" data-bs-toggle="dropdown">Menu</button>
All data-* attributes for Bootstrap components follow this pattern (data-bs-target instead of data-target, data-bs-dismiss instead of data-dismiss, and so on). This is a minor syntax change, but important for upgrading your markup. Overall, the removal of jQuery means a cleaner setup and potentially better performance, since developers can avoid loading an extra library. As the Bootstrap team put it, “no more jQuery!” indicating a move toward modern JavaScript practices.
Bootstrap’s grid system is central to building layouts, and it saw a few updates in version 5:
New Breakpoint “xxl”: Bootstrap 5 adds a new largest breakpoint, xxl, for very large screens. This breakpoint kicks in at 1400px and up, expanding beyond the previous largest (xl which was ≥1200px). All other breakpoints remain the same as in v4 With xxl, you can now target ultra-wide screens (like large desktops or TVs) with specific column layouts if needed. The addition of xxl also means the container max-width for that tier is larger (for example, the max width of a .container-xxl is 1320px, whereas .container-xl was 1140px in v4).
Gutters in rem and New Gutter Utilities: The space between grid columns (gutters) has been refined. In Bootstrap 4, the default gutter width was 30px (15px on each side of a column). In Bootstrap 5, gutters are slightly narrower by default – 1.5rem (about 24px) – which aligns them with the spacing scale used by utilities More notably, Bootstrap 5 introduces explicit gutter utility classes to control this spacing:
.g-* classes can be used on the row to set uniform gutters (for example, .g-0 for no gutter, .g-3 for a medium gutter, etc., with values from 0 to 5)..gx-* and .gy-* classes allow controlling horizontal and vertical gutter spacing independently For instance, .gx-2 could add horizontal gutters while leaving vertical spacing at default.In Bootstrap 4, if you wanted to remove gutters, you would use the class .no-gutters on the row. This class is removed in Bootstrap 5 – you’d now use .g-0 to achieve the same effect For example:
<!-- Bootstrap 4: no gutters between columns -->
<div class="row no-gutters">
<div class="col">...</div>
<div class="col">...</div>
</div>
<!-- Bootstrap 5: no gutters using .g-0 -->
<div class="row g-0">
<div class="col">...</div>
<div class="col">...</div>
</div>
This new gutter utility approach is more flexible – you can even increase gutters by using higher numbers, or remove only horizontal or only vertical spacing as needed.
Refinements to Columns: In Bootstrap 5, grid columns no longer have position: relative applied by default In Bootstrap 4, every .col-*-* element was relatively positioned (which was mainly useful if you needed to position something absolutely within a column). Removing this reduces unnecessary CSS. However, if your layout relied on that positioning (e.g., absolutely positioning an element inside a column), you will now need to add .position-relative to that column manually to restore the behavior. This is a small change but worth noting for migrations.
Dropped Support for Some Grid Classes: Bootstrap 5 trimmed some less-used grid classes. For example, the .order-* classes for ordering columns have been reduced; v5 only includes .order-1 through .order-5 out of the box, instead of up to .order-12 as in v4 The idea is that very high order numbers were rarely used. If needed, additional orders can be added via custom CSS or the utility API. Similarly, the old .embed-responsive classes for responsive iframes were replaced by a new .ratio utility (we’ll touch on that in Utilities).
In practice, the grid in Bootstrap 5 is mostly familiar if you know Bootstrap 4. The introduction of the xxl breakpoint simply gives you one extra tier to design for, and the new gutter utilities give you more control over spacing. For example, you might now easily create a layout with no vertical gap but a horizontal gap between columns using classes like .gx-4 .gy-0 – something that was trickier in v4. Other core concepts (12-column system, flexbox-based grid) remain the same. The adjustments made (like gutter sizing and removing .no-gutters) are aimed at making layouts more consistent and easier to customize. Overall, these changes streamline the grid and tie it more closely to the spacing utility system of Bootstrap.
Bootstrap 5 introduces a powerful Utility API that changes how additional utility classes can be generated and customized. In Bootstrap 4, the framework provided a set of utility classes (for spacing, text alignment, colors, etc.) and if you wanted to add your own or modify them, you often had to override CSS or use Sass variables/loops. With the new Utility API in v5, developers can leverage Sass maps to generate utility classes on the fly as part of their custom builds
In simple terms, the Utility API is a Sass-based configuration that lets you define new utilities (or modify existing ones) in Bootstrap's source. It’s now the primary way to extend Bootstrap’s default utility classes For example, if you wanted to create a set of utility classes for a CSS property like text-shadow, you could add an entry to the $utilities map in Bootstrap’s Sass, and Bootstrap will output classes for you (e.g., .text-shadow-sm, .text-shadow-lg, etc.) without you writing the CSS for each variation. You can configure things like the class prefix, the CSS property it applies to, responsive variants, states (like :hover), and print variants This system provides a standardized way to generate utility classes, ensuring consistency.
Bootstrap 4 did allow enabling or disabling certain utility classes via Sass variables (e.g., $enable-padding), but adding entirely new types of utilities was not as straightforward. In Bootstrap 5, by using the Utility API, you have a “language” for it in Sass – making Bootstrap more of a utility-first toolkit if you want it to be. According to the Bootstrap team, this approach was inspired by the popularity of utility-first CSS frameworks and is meant to give developers fine-grained control over what utilities exist in their project
Out-of-the-box Utilities: Even if you don’t dive into the Sass API yourself, Bootstrap 5 comes with a lot of new built-in utility classes that weren’t in Bootstrap 4 Some examples include:
.top-0, .bottom-0, .start-50, .translate-middle etc., to position elements (taking advantage of CSS top/right/bottom/left and translate for centering)..d-grid class to set an element to display: grid (in addition to the flex and inline utilities from v4), and a related .gap-* utility to control the gap between grid items .fs-1 through .fs-6 for setting font-size based on heading sizes, and .fs-base for the default font size..font-weight-bold. In v5, these are shortened to .fw-bold, .fw-semibold, etc., for brevity .rounded-1, .rounded-2, .rounded-3 to apply smaller or larger border-radius values than the default .rounded (which is medium) .overflow-visible and .overflow-scroll to control element overflow And many more – Bootstrap 5’s documentation lists a plethora of utilities that cover common needs (flexbox, spacing, sizing, positioning, text, backgrounds, etc.), often expanding on what v4 provided.
The advantage for developers is that you can often avoid writing custom CSS for one-off styles, because there’s likely a utility class for it in Bootstrap 5. And if there isn’t, the Utility API makes it relatively easy to add your own in a maintained way. This means faster development and a more consistent codebase.
Overall, the Utility API is more of an “under the hood” feature if you just use precompiled CSS, but it’s a game changer for those who customize Bootstrap via Sass. It provides a structured way to maintain and scale utility classes in a project Even for those not using Sass, the presence of many new utility classes in Bootstrap 5 will be felt as a convenience in day-to-day usage.
Forms got a significant overhaul in Bootstrap 5, both in documentation structure and in the framework itself. In Bootstrap 4, forms had two parallel systems: the default browser-styled forms and a set of custom form controls (for checkboxes, radios, switches, file inputs, ranges, etc.) that required additional markup. This sometimes led to confusion and extra HTML elements for achieving a consistent look. Bootstrap 5 simplifies this by going fully custom for all form elements while also simplifying the required markup
Key changes and improvements in Bootstrap 5 forms include:
Unified Form Controls: All form controls (text <input>s, <select>, <textarea>, checkboxes, radios, switches, file inputs, ranges, etc.) have a unified, custom-designed appearance by default. Bootstrap 4’s approach was to introduce “custom” form controls alongside the default ones (for example, .custom-checkbox for a fancy checkbox). In v5, there’s no separate “custom” set – everything is styled via Bootstrap’s CSS to look harmonious across browsers This means you get consistent, modern styling for even the most basic <input type="text" class="form-control"> as well as for checkboxes and radios, without needing extra wrapper elements.
Simplified Markup for Checkboxes/Radios: In Bootstrap 4, a custom checkbox required a structure like:
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="myCheck">
<label class="custom-control-label" for="myCheck">Check</label>
</div>
In Bootstrap 5, this is simplified to:
<div class="form-check">
<input type="checkbox" class="form-check-input" id="myCheck">
<label class="form-check-label" for="myCheck">Check</label>
</div>
You no longer need that extra wrapper with a special class for custom controls – .form-check is just a generic wrapper (often a <div>) for grouping a checkbox/radio and its label. The classes .form-check-input and .form-check-label on the <input> and <label> are what apply the Bootstrap styling. This change makes the HTML more concise and semantic, with no superfluous markup – just form controls and labels The result is a much more consistent look and feel, as noted in the Bootstrap blog: all those elements now share the same styles across different browsers and operating systems
Floating Labels: Bootstrap 5 introduces floating label support, which is a new pattern for forms. Floating labels are labels that appear inside the form control (as placeholder text) and then animate/move above the field when it is focused or filled. This pattern was not natively supported in Bootstrap 4 (developers had to implement it manually or via third-party CSS). In Bootstrap 5, you can simply wrap a form control in .form-floating along with its <label>, and Bootstrap’s CSS will handle the rest For example:
<div class="form-floating">
<input type="email" class="form-control" id="inputEmail" placeholder="name@example.com">
<label for="inputEmail">Email address</label>
</div>
This will render a text field with a label that floats up when the user types. It’s a sleek UI enhancement that’s now easy to implement out-of-the-box.
Improved File Input: In Bootstrap 4, custom file inputs were notoriously tricky, requiring a .custom-file wrapper and some JavaScript to show the selected file name. Bootstrap 5 streamlined this by dropping the .custom-file approach and just using the standard .form-control class on file inputs All the styling is done via CSS, and no extra JS is needed to display the filename – the file input’s default behavior (showing the filename next to the control) is used and styled. This makes file input markup much cleaner and reduces the amount of code.
Form Layout Classes Removed: Classes that were used for form layout in v4 have been removed in favor of the grid and utilities. Notably, .form-group (which was a simple wrapper that applied margin-bottom), .form-row (a specialized row for forms), and .form-inline (for inline forms) are all dropped In Bootstrap 5, you achieve the same layouts by using the standard grid classes (.row and .col-*) or utility spacing classes (like .mb-3 on form groups to space them out). For inline forms, you can just use flex utilities or the new grid. Essentially, the reasoning is to avoid having duplicate systems for layout – just use the robust grid & utility system that Bootstrap provides. For instance, instead of wrapping inputs in <div class="form-group">...</div> for spacing, you might simply add a utility class like .mb-3 to the input or label for margin. This change may require some updates when migrating, but it simplifies the framework (one less set of classes to remember).
Validation Feedback: While not a headline feature, Bootstrap 5 also refined form validation styles. The colors for validation (green for success, red for error) were adjusted slightly for better contrast, and the icons that were used in some cases were removed in favor of simpler indicators (because Bootstrap 5 doesn’t include Glyphicons or any icon by default – though you can use Bootstrap Icons separately). The validation tooltips/popovers also no longer require jQuery, as with other components.
Overall, forms in Bootstrap 5 are more consistent and require less boilerplate. Every form control has been “deduplicated” in the sense that there’s no separate markup for a custom vs default version – there’s just one way to do a checkbox, one way to do a radio, etc., and they all match visually The introduction of floating labels and other tweaks make modern form designs easier. If you’re upgrading, you’ll primarily be removing those extra wrappers and .custom-* classes and ensuring your form elements have the correct new classes (.form-control, .form-check-input, etc.). The end result is cleaner HTML and a more polished form UI out of the box with Bootstrap 5.
Bootstrap 5 not only removes some old things but also adds and improves a number of components to make the framework more comprehensive and modern. Here are some of the notable component changes:
New Offcanvas Component: Offcanvas is a brand new component in Bootstrap 5, which did not exist in Bootstrap 4. An offcanvas is like a sidebar panel that overlays or slides in from the side of the screen (or top/bottom) and contains navigational links or other content. It’s typically toggled by a button, similar to how a modal is triggered. The offcanvas component is built to be flexible – it can be positioned on the left, right, top, or bottom of the viewport, and comes with options for backdrop and scroll behavior This is great for building mobile menus or slide-out panels (for example, a shopping cart preview on an e-commerce site). Previously, developers often used custom code or third-party scripts to achieve offcanvas menus in Bootstrap 4; now it’s a first-class citizen in Bootstrap 5. You simply add the attribute data-bs-toggle="offcanvas" to a trigger button and the appropriate .offcanvas markup for the panel. For instance, a basic offcanvas for a nav menu might look like:
<button class="btn btn-primary" data-bs-toggle="offcanvas" data-bs-target="#myOffcanvas">Toggle Menu</button>
<div class="offcanvas offcanvas-start" id="myOffcanvas">
<div class="offcanvas-header">
<h5>Menu</h5>
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas"></button>
</div>
<div class="offcanvas-body">
<!-- Offcanvas content like nav links here -->
</div>
</div>
Offcanvas uses the same JavaScript methods as modals and other components, meaning you can control it via JavaScript or data attributes. It’s a welcome addition because it enables common UI patterns (like collapsible sidebars) without extra plugins
Accordion Component Revamp: In Bootstrap 4, “accordions” were usually implemented using the Collapse component and some custom markup (often with cards). Bootstrap 5 introduces a dedicated Accordion component that simplifies the HTML structure and brings consistency. The new accordion uses .accordion as a wrapper, and each collapsible item is an .accordion-item containing an .accordion-header and an .accordion-collapse (for the collapsible content) with an .accordion-body inside For example, an accordion item in v5 looks like:
<div class="accordion-item">
<h2 class="accordion-header" id="headingOne">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne">
Accordion Item #1
</button>
</h2>
<div id="collapseOne" class="accordion-collapse collapse show" data-bs-parent="#myAccordion">
<div class="accordion-body">
<!-- Content here -->
</div>
</div>
</div>
In contrast, in Bootstrap 4 you might have had to use card components to achieve a similar effect. The new accordion component in v5 also comes with some handy options: for example, adding the class .accordion-flush to the accordion removes the default background color and borders, giving a minimal, edge-to-edge style This is useful if you want the accordion sections to blend with the parent container. The accordion headers in Bootstrap 5 accordions use <button> elements with the class .accordion-button, which by default includes a caret icon that rotates when open/closed, and it’s also designed to be accessible (properly tied to the collapsible content with aria-controls etc.). Essentially, Bootstrap 5’s Accordion is easier to implement (no custom card markup) and provides a better out-of-the-box UI for expandable sections.
Modal, Tooltip, Popover (JavaScript updates): While the visual markup of modals, tooltips, and popovers remains similar, their underlying JavaScript saw improvements. With jQuery gone, these components have new static methods and events. For instance, tooltips and popovers in Bootstrap 5 can be optionally enabled globally with a few lines of JS (using Tooltip or Popover classes) and they now use Popper v2 for positioning which improves their placement logic Most of these changes are behind the scenes, but one thing a developer might notice is that data attributes are namespaced (as mentioned earlier, e.g., data-bs-toggle="modal" for modals). Also, Bootstrap 5 standardized event naming (still using events like shown.bs.modal etc., but since jQuery isn’t used to trigger them, you’d attach them with element.addEventListener).
Dropdowns and Menu Items: Dropdowns got a couple of useful enhancements. The JavaScript for dropdown positioning now uses Popper v2 which makes dropdowns smarter about staying within viewport. Additionally, dark-themed dropdowns are now easy to create by adding the class .dropdown-menu-dark to a dropdown menu container This inverts the dropdown’s colors to use a dark background with light text, which is handy for dark navbars or dark mode designs. In Bootstrap 4, one had to customize CSS to achieve a dark dropdown; now it’s built-in. There’s also a new .dropdown-header styling and improved support for dropdown items as forms or buttons. Similarly, carousels have a .carousel-dark variant now for darker controls and indicators on light backgrounds
Navbar Updates: The Bootstrap 5 navbar component is largely the same as v4 in structure, but there are a few tweaks. One addition is .navbar-nav-scroll: when added to a <ul class="navbar-nav"> inside a vertical scrolling offcanvas or collapse, it will give that area a vertical scrollbar if the content is tall, making long menus usable within a limited height container This is useful for scenarios with many nav links. Also, since jQuery is gone, the navbar toggle uses the new data attributes (data-bs-toggle="collapse" etc.), but that’s covered by the general changes we discussed.
Buttons: The .btn-block class is removed in Bootstrap 5 In Bootstrap 4, .btn-block made a button span the full width of its parent. In v5, you can simply use the utility classes to achieve the same effect – for example, add .w-100 (100% width) to your button, or wrap a group of buttons in a .d-grid with an appropriate column size. The decision to remove .btn-block was to reduce redundancy since utilities cover the use case. For instance:
<!-- v4 -->
<button class="btn btn-primary btn-block">Submit</button>
<!-- v5 equivalent -->
<button class="btn btn-primary w-100">Submit</button>
Both will render a full-width button. This is a small change, but if you migrate you’ll need to replace .btn-block with .w-100 (or another approach).
List Group: Bootstrap 5 adds a new modifier class for list groups: .list-group-numbered. This will automatically add number indices (1, 2, 3, ...) before each list group item using CSS ::before pseudo-elements It’s a quick way to create an ordered list appearance while still using the <ul class="list-group"> structure. Additionally, list groups in v5 can be used with new utility classes for horizontal layouts (using flex utilities) rather than a dedicated .list-group-horizontal class for each breakpoint as in v4 (though .list-group-horizontal is still supported for a specific use).
Close Button: The dismissible “close” icon (an “×”) used in alerts, modals, offcanvas, etc., is implemented differently in Bootstrap 5. Instead of an <button class="close"> with a × text that was styled (and some screenreader text), Bootstrap 5 provides a .btn-close class on a <button> element This outputs a small, typically gray, close icon using an embedded SVG via CSS. The new close button has no inner text (the accessible name is provided by aria-label="Close"). This change was made to improve accessibility and to align with a more modern approach (many frameworks now use an SVG icon for a close button). As a developer, you’ll use:
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
instead of the old
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>.
The new .btn-close is styled and ready to go (with a default dark “×” icon or a white icon if you add .btn-close-white for use on dark backgrounds).
Removed Components: Bootstrap 5 has removed a couple of components that were deemed redundant or better handled via utilities:
.jumbotron provided a big padded grey box for marketing headings. In v5, you can simply use spacing utilities, a background color utility (or custom CSS), and maybe a <h1> to create the same effect. The reasoning is that a jumbotron is essentially just a big padded section, which is easily done without a dedicated component. In fact, the Bootstrap 5 docs provide an example of how to recreate a jumbotron using utilities and standard classes .media class), which was used for aligning images or icons next to content (e.g., a list of comments with avatars), has been removed This was a holdover from older Bootstrap versions. In v4, .media and related classes helped with a certain kind of layout, but in v5, the layout can be achieved with the flex utilities (.d-flex, .align-items-*, etc.) very easily. So, rather than keeping a special component, they dropped it to encourage using the generic flexbox approach. If you were using the media object, you’ll transition to using a <div class="d-flex"> containing an image and a content div, for example, and you can get the same result with more flexibility.Icons (separate): While not part of Bootstrap 5’s core CSS, it’s worth noting that alongside Bootstrap 5, the team introduced Bootstrap Icons, a separate SVG icon library. In the Bootstrap 5 docs and examples, you might see references to icons (for example, in the Accordion they show chevron icons using Bootstrap Icons, and in alerts they demonstrate adding an SVG icon). These are not included by default with Bootstrap 5, but they are easy to include if you need a consistent icon set. This is more of an ecosystem addition than a framework difference, but it complements the new components.
In summary, Bootstrap 5 cleaned up some older components and introduced new ones to cover gaps. Features like offcanvas and accordion make it easier to build modern interfaces without third-party plugins Dropping things like jumbotron and media streamlines the framework – those can be accomplished with utilities now, reducing bloat. As you upgrade or start new projects, these component changes mean you have more tools at your disposal (offcanvas, accordion, etc.) and also a bit of refactoring for things that went away (using utilities in place of jumbotron/media, etc.). The overall component set in Bootstrap 5 is more versatile while also being more consistent with the rest of the framework.
Under the hood, Bootstrap 5 made numerous changes to its CSS and Sass, improving theming capabilities and modernizing the codebase:
Color System Enhancements: Bootstrap 4 provided a limited set of theme colors (primary, secondary, success, danger, etc.) and used Sass functions like darken() and lighten() to derive hover colors or border colors. In Bootstrap 5, the color system was overhauled. They introduced a expanded palette of shades for each color and switched to a different method of generating variants. Instead of darkening or lightening by fixed percentages (which could sometimes produce low-contrast results), Bootstrap 5 uses a tint/shade approach – mixing a color with white or black to get variants In fact, for each of the core colors, Bootstrap 5 now provides nine separate tones (for example, for primary: primary-color, primary-subtle, etc., ranging from a very light tint to the base color to a very dark shade). These are output as Sass variables and some as CSS custom properties. The result is a richer default color palette and better contrast. They also improved color contrast across the board – for instance, the contrast ratio of text on background was increased from ~3:1 in v4 to 4.5:1 in v5 for accessibility compliance Some colors like the default blue, green, etc., were adjusted to meet WCAG 2.1 AA guidelines Additionally, the $theme-colors Sass map (which defines the named theme colors) can no longer be partially merged – you define all your theme colors explicitly if you override it, which is intended to make customization clearer (previously it was easy to forget to include a color in a merge). For developers, this means you have more predefined color classes (e.g., .text-primary, .bg-primary still exist, but now also things like .bg-primary-subtle in newer versions) and a more robust system if you generate your own theme variants.
CSS Custom Properties (CSS Variables): One big change enabled by dropping IE is the adoption of CSS variables in Bootstrap’s compiled CSS. Bootstrap 5 includes a growing number of CSS custom properties (prefixed with --bs-) for various components and layout elements For example, the new Ratio helper (which replaced the embed aspect-ratio utility) uses a CSS variable --bs-aspect-ratio on the .ratio element to set the aspect ratio percentage You can change the aspect ratio by overriding that variable in your own CSS (e.g., style="--bs-aspect-ratio: 50%;" for a 1:2 ratio box). Also, things like colors are exposed as CSS vars in some places (especially as of Bootstrap 5.1 and 5.2, more and more of the theme colors and component colors have CSS variable equivalents). For instance, the .table component uses CSS variables for its border color, etc., making it easier to restyle tables by overriding those vars In the context of Bootstrap 5.0 vs 4.6, the initial release of v5 already included CSS variables for components like forms, tables, tooltips, modals, etc., to allow runtime theming. This is a step toward easier theming without needing Sass recompilation – you can adjust a lot by simply setting new values for these custom properties in the :root or a particular element. The documentation even introduced a CSS Variables section to list out all the available custom properties
Sass Compiler Change (Dart Sass): Bootstrap 5 moved from using LibSass (which was the engine behind Node Sass) to Dart Sass as the Sass compiler LibSass was deprecated, so this change was necessary to keep Bootstrap’s build process up to date. For most users using precompiled CSS, this doesn’t matter directly. But if you compile Bootstrap from source, you might need to ensure your build tools use Dart Sass. Dart Sass is also stricter in some ways, which influenced a few code changes (for example, certain deprecated Sass features had to be removed).
Sass Code Changes: There were various Sass variable and function name changes aimed at clarity and modernization:
color-yiq to color-contrast – reflecting that it’s not using the YIQ color model anymore but a more standard contrast approach.color(), theme-color() and gray() (which in Bootstrap 4 let you get colors from the Sass maps) were removed in favor of using Sass variables or the new shade-color()/tint-color() utilities In short, you now use the variables directly (like $primary, $secondary) instead of calling a function to retrieve them.$enable-* flags (which in v4 allowed you to disable certain components or features) were mostly removed or changed. For example, $enable-jquery was removed since jQuery is no longer needed. $enable-grid-classes no longer disables container generation (containers are always generated now) hover() or text-hide()) were dropped entirely Also, the badge-pill class in v4 (which made badges more rounded) was removed – in v5 all badges are by default more pill-shaped, and for further customization you’d use .rounded-pill if needed.$color-rgb maps for each color) and the utilities API as mentioned.Root Customization and Enhanced Theming: Bootstrap 5 provides a new Customize section in the docs that replaces the old theming docs This section guides how to override Sass maps or use CSS variables to theme your site. For instance, you could override $primary Sass variable to change your primary color, or in Bootstrap 5.2+, just set --bs-primary CSS variable in the :root. The framework aims to make theming easier by exposing internal values. In v5.0, not every component had CSS vars for every property, but the groundwork was laid – for example, all components list their Sass variables in documentation now so you know what to override. By dropping IE, Bootstrap can lean on modern CSS for theming, which is a big win for developers who want to customize Bootstrap’s look.
In summary, Bootstrap 5’s CSS/Sass changes give developers better tools for customization and ensure the framework is using modern best practices. The improved color system yields a more accessible UI by default (higher contrast) and the introduction of CSS variables means you can do things that were impossible with Bootstrap 4 – like dynamically change theme colors at runtime (for example, implementing a dark mode by toggling a few CSS variables, rather than swapping an entire CSS file). Sass power-users will find the new approach requires some adjustments (e.g., adopting Dart Sass, using new function names, etc.), but these changes are well-documented and generally make the Sass code more logical. Overall, these updates ensure that Bootstrap 5 can be more easily customized and integrated into modern web projects.
As part of its modernization, Bootstrap 5 officially dropped support for Internet Explorer 10 and 11, as well as some other older browsers. In Bootstrap 4, IE10+ was supported (with some polyfills); in Bootstrap 5, the team made a clean break. Specifically, Bootstrap 5 no longer supports IE 10/11, Legacy Edge (the non-Chromium version of Microsoft Edge), and really old versions of other browsers (Firefox < 60, Chrome < 60, Safari < 12, iOS Safari < 12, to name a few) Essentially, the baseline is latest two versions of major browsers and anything with full ES6/CSS variable support.
Dropping IE was a deliberate choice to allow the usage of modern CSS like CSS variables, flexbox gaps, and so on, without worrying about providing fallbacks or encountering IE-specific bugs. It also lets Bootstrap use modern JavaScript (ES6) features in its code since it can assume a minimum level of JS engine capability. This improves development velocity and output file size – there’s simply less code needed for compatibility.
For developers, this means if you have to support users on IE11, Bootstrap 5 will not be suitable out of the box. You would either have to stick to Bootstrap 4 or find workarounds (like some community “Polyfill” scripts, but even those can’t fully patch all the gaps). The Bootstrap team’s perspective is that by now (a few years into Edge/Chrome/Firefox dominance), most projects can drop IE support. In fact, if you look at the market share, IE11 usage has become minimal, and many other libraries have also dropped IE support. So Bootstrap 5 aligns with that trend, focusing on modern browsers.
The positive side for those who can drop IE is that Bootstrap 5 can use cutting-edge CSS. For example, Bootstrap 5 uses the :focus-visible pseudo-class in its CSS (with a polyfill for browsers that don’t support it yet, but IE wouldn’t get it at all). It also means that the layout and components might render more consistently (since IE was often the odd one out that needed special fixes).
One immediate effect of no IE support: file size and performance benefits. No more IE-specific CSS hacks or scripts means leaner code. For instance, Bootstrap 4 had some workarounds for IE flexbox bugs – those are gone now. Also, by using CSS variables and not worrying about IE, certain theming features become possible (which we discussed above).
To summarize, Bootstrap 5 is intended for a world without IE. It assumes your users have a modern Evergreen browser. If your project has a user base that still significantly includes IE11, you might need to remain on Bootstrap 4 (the Bootstrap 4.6 branch will still exist for a while for extended support). Otherwise, moving to Bootstrap 5 will simplify your life by removing the need to test in IE and allowing you to use more modern CSS/JS techniques freely. This was considered “one of the biggest leaps” in the project in a long time but it is in line with web development’s direction.
(Side note: Along with IE, Bootstrap 5 also dropped support for older Android 6 (the WebView browser) and iOS 10 Safari, etc. These are generally in the same category of "older, outdated platforms". Most up-to-date mobile devices are fine with Bootstrap 5.)
With the various changes mentioned (no jQuery, cleaned-up CSS, dropped IE), Bootstrap 5 comes with performance enhancements compared to Bootstrap 4. These improvements manifest in both file size and runtime behavior:
Reduced File Size: Thanks to removing jQuery and some older plugins, as well as refactoring the code, Bootstrap 5’s default bundle can be notably smaller. In one comparison, the combined size of Bootstrap 5’s CSS and JS was about 50% smaller than Bootstrap 4’s (153 KB vs 308 KB, presumably measured minified) That’s a significant reduction, which means faster downloads for your users. Even though some of that size drop is the removal of jQuery (which itself was ~30 KB gzipped), the Bootstrap JS itself is also more compact since it’s using modern JS. Every bit of size reduction helps improve page load times, especially on mobile devices or slower networks.
Faster Loading and Parsing: Fewer dependencies and a smaller bundle mean the browser has less to load and parse. Bootstrap 5’s JavaScript, written in plain ES6, can be executed more directly by modern browsers’ JS engines (potentially yielding quicker initialization of components). For example, one source noted pages using Bootstrap 5 achieved roughly one-third faster “time to interactive” in their tests, compared to the same pages on Bootstrap 4 This can vary based on what exactly your page is doing, but the elimination of jQuery’s overhead in initializing components (and no need to wait for jQuery to load) certainly plays a role.
Optimized CSS and Rendering: Removing support for older browsers allowed the team to simplify CSS rules. A simpler CSS (with modern features) can sometimes mean the browser has an easier time computing styles and layouts. For instance, using CSS variables can reduce repetition in CSS, and properties like gap in flexbox/grid (which Bootstrap 5 can use knowing it doesn’t need to cater to IE) can achieve layouts without extra wrapper elements or negative margins. All these can make reflows and repaints more efficient during runtime. It’s hard to quantify framework-specific rendering speed, but generally, Bootstrap 5 was built to be more efficient.
Popper.js v2: Bootstrap’s dropdowns, tooltips, and popovers rely on Popper for positioning. Popper v2 is used in Bootstrap 5, which is smaller and faster than Popper v1 (used in Bootstrap 4). This contributes to a smaller JS footprint and potentially quicker dropdown positioning logic.
Improved Asset Loading: If you use Bootstrap 5 via its npm package or a bundler, you’ll benefit from an improved module structure. Also, if you only need certain parts of Bootstrap (say just the CSS and maybe one JS plugin), it can be tree-shaken or imported modularly more easily than with Bootstrap 4’s setup. This means your build could include only what you need, reducing file size further.
Real-world impact: The performance gains from switching to Bootstrap 5 might not make a slow site instant, but they give a nice boost – especially if your project was very Bootstrap-heavy. With roughly half the file size and no jQuery, you cut down on resource requests and processing Faster load and execution times lead to a better user experience (and even SEO benefits, since search engines favor faster sites). For example, mobile users on 3G might notice that the site becomes usable a few hundred milliseconds sooner, which is valuable.
In summary, Bootstrap 5’s leaner architecture translates into speed. It aligns with the broader industry shift of moving away from monolithic frameworks (like jQuery) to more efficient, specialized code. If performance is a concern, upgrading to v5 is a smart move – you get the same functionality with less weight and often less complexity, which is a win-win for both developers and users
Upgrading an existing project from Bootstrap 4 to Bootstrap 5 requires some planning since there are breaking changes. Here are important considerations and tips to ensure a smooth transition:
Read the Official Migration Guide: Bootstrap provides a detailed migration guide in their documentation, outlining changes from v4 to v5 This should be your checklist. It lists all the class name changes, component removals, and behavioral changes. Go through it to identify which parts of your project will be affected. Major sections include changes in dependencies, the grid, forms, components, utilities, etc.
Update CDN/References: If your project links to Bootstrap CSS/JS via CDN or local files, update those references to Bootstrap 5 files. The directory names or filenames might have changed (for example, there is no separate bootstrap.bundle.js with jQuery in v5, instead there’s a bootstrap.bundle.js that already includes Popper v2). Also update Popper to v2 if you were including it separately for tooltips.
Adapt HTML Markup and Classes: Go through your HTML and change any deprecated Bootstrap 4 classes to their Bootstrap 5 equivalents:
<div class="form-group"> should be removed or replaced with a simple <div> (and use spacing utilities if needed for margin). <form class="form-inline"> can usually be replaced with just <form class="row gy-2 gx-3 align-items-center"> or other utility-based approach to make form fields inline..ml-3 becomes .ms-3, .mr-3 becomes .me-3 (and similarly for padding .pl-*, .pr-* to .ps-*, .pe-*). Ensure you rename these in your code .text-black-50 and .text-white-50 stayed the same, but the .text-* classes no longer apply to links on hover (they removed that behavior). There's also a new set of .link-* classes if you want colored links specifically. This is minor unless you used those..jumbotron in v5. If you have one, you can mimic it by adding equivalent padding and background classes. For example, <div class="p-5 mb-4 bg-light rounded-3"> on a container can resemble a jumbotron (this is basically what the new example in docs suggests)..media class. If you used the media object, refactor it using flexbox utilities. For instance, <div class="d-flex align-items-start"> <img ... class="me-3"> <div>...</div> </div> would replace a media object (image with content to its right)..badge-pill in v5 – all badges are pill-shaped by default (with border radius). If you had .badge-pill, you can remove it or replace with .rounded-pill (which is a generic utility for fully rounded corners) to explicitly achieve a pill shape..btn-block with .w-100 on buttons for full width <div class="custom-control"> wrappers; use the simpler .form-check structure for checks and radios. Change <input class="form-control-file"> (from v4) to just <input class="form-control"> for file inputs (v5 styles file inputs as part of form controls) If you used .form-row, use .row with perhaps .g-2 gutter classes. If you used .form-inline, likely just use flex or grid to lay out inline..float-left and .float-right are still there in v5 (as .float-start and .float-end respectively). So change those if used. .sr-only (screen reader only text) is renamed to .visually-hidden – update that for any accessibility text you hide.data-toggle="collapse" on the toggler to data-bs-toggle="collapse", etc., as noted. The rest of navbar classes largely remain same.data-toggle=, data-target=, data-dismiss=, data-spy= etc. and prepend data-bs- to them E.g., data-toggle="modal" → data-bs-toggle="modal", data-target="#myModal" → data-bs-target="#myModal", data-dismiss="alert" → data-bs-dismiss="alert". Also, Bootstrap 5 dropped the scrollspy data-spy attribute in favor of data-bs-spy. Ensure all these attributes on your components are updated, otherwise they won’t work.Update JavaScript Usage: If you have custom scripts that utilized Bootstrap’s JS via jQuery, these need refactoring:
$('#myTab').on('shown.bs.tab', function () { ... }). In v5 without jQuery, you attach events directly: document.getElementById('myTab').addEventListener('shown.bs.tab', function () { ... });. The event names (like shown.bs.tab) remain the same, but they are plain DOM events now. Also remember to update any Event.namespace usage because without jQuery, you’ll be dealing with the standard Event object.$('.toast').toast('show') becomes something like:
var toastEl = document.querySelector('.toast');
var toast = new bootstrap.Toast(toastEl);
toast.show();
The migration guide and documentation show the equivalent code for each plugin. Alternatively, rely on data attributes more – e.g., a toast with data-bs-autohide="false" will configure itself, and you might only need to trigger show() via JS.
fetch or Axios instead of $.ajax if you drop jQuery entirely, but that’s outside Bootstrap’s scope).Leverage the Bootstrap 5 Migration Tool: There is a community-provided tool (command-line script) that can automate many of these find-and-replace operations in your code For example, it can scan your project and:
.no-gutters to .g-0, .btn-block to .w-100, etc.)..jumbotron for equivalent utility classes, or a .media for flex classes) data-bs-* Using such a tool can save time and catch things you might overlook. Of course, you should still test everything afterwards, but it provides a good automated first pass.
Testing and Iteration: After making the bulk of the changes, test your pages. Pay special attention to:
It might be helpful to do the migration in a branch or a test copy of your project, especially if it’s large. Migrate one section at a time, get it working, then move on.
Optional: Embrace New Features: Once things are working equivalently to before, you can start using new Bootstrap 5 features in your project. For example, maybe replace some custom workaround you had (like a custom sidebar script) with the new Offcanvas component, or use the accordion component where you previously didn’t have a good solution. This isn’t required for migration, but part of the value of upgrading is being able to simplify your code by using the new tools provided by Bootstrap 5. Also consider using the expanded color palette or new utility classes to clean up any custom CSS.
When to Not Migrate: If you discover that you absolutely must support an older browser (say, your analytics show a significant IE11 user base, or perhaps an older Safari on devices), you might decide to postpone migrating to v5. As one article noted, if you need IE10/11 or you heavily depend on jQuery-specific code, you might stick with Bootstrap 4 for now Security updates for v4 will likely continue for a while, but no new features will be added there. Weigh the pros and cons for your situation.
Migrating from Bootstrap 4 to 5 is definitely easier than the earlier migration from 3 to 4 was. Many class names are unchanged, and the overall concepts are the same. It’s mostly find-replace for class and attribute names, removing some wrappers, and ensuring your JS is updated for the new APIs. By following the guide and using the available tools, you can transition relatively smoothly. Once done, you’ll be able to enjoy all the enhancements of Bootstrap 5 while your site continues to function as expected.
To illustrate the differences between Bootstrap 4 and Bootstrap 5, here are several side-by-side code examples showing old vs new syntax and best practices:
Modal Initialization (Bootstrap 4 vs Bootstrap 5):
In Bootstrap 4, using jQuery was the common way to trigger a modal:
// Bootstrap 4: open a modal via jQuery
$('#myModal').modal('show');
In Bootstrap 5, without jQuery, you use the Modal class provided by Bootstrap's JS:
// Bootstrap 5: open a modal via Vanilla JS
var myModalEl = document.getElementById('myModal');
var myModal = new bootstrap.Modal(myModalEl);
myModal.show();
Here, we select the modal element and create a new bootstrap.Modal instance. We then call the show() method to open it. This pattern (get element, pass to constructor, then call methods) is used for other components like dropdowns, tabs, tooltips, etc., in v5. You can see no jQuery is needed – this is pure JavaScript. (Note: Bootstrap 5 also automatically wires up modals if you use the data-bs-toggle="modal" approach on triggers, but this is how to do it in code.)
Dropdown Toggle in HTML:
In Bootstrap 4, the toggler for a dropdown menu might be:
<!-- Bootstrap 4 dropdown button -->
<button class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown" aria-expanded="false">
Dropdown
</button>
The data-toggle="dropdown" attribute activates the dropdown. In Bootstrap 5, this needs to be data-bs-toggle:
<!-- Bootstrap 5 dropdown button -->
<button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
Dropdown
</button>
This is a small but crucial change. The same goes for anything using data-target (becomes data-bs-target), data-dismiss (to data-bs-dismiss), etc. If you forget to update these, your components won’t work in v5. The above shows the new attribute in action for a dropdown.
Grid Gutters (spacing between columns):
Suppose you want two columns with no gutter between them. In Bootstrap 4 you’d do:
<!-- Bootstrap 4 no-gutter row -->
<div class="row no-gutters">
<div class="col-6">Column 1</div>
<div class="col-6">Column 2</div>
</div>
In Bootstrap 5, the .no-gutters class is replaced by the utility .g-0:
<!-- Bootstrap 5 no-gutter row -->
<div class="row g-0">
<div class="col-6">Column 1</div>
<div class="col-6">Column 2</div>
</div>
This will produce two half-width columns with no spacing between them. If, alternatively, you wanted extra-large gutters, in v5 you could use .g-4 or .g-5 on the row. In v4, there wasn’t an easy built-in way to increase gutters beyond the default except writing custom CSS – now it’s just a class.
Custom Checkboxes:
Let’s compare the markup for a checkbox input in a form:
Bootstrap 4 (custom checkbox):
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="check4">
<label class="custom-control-label" for="check4">Check this custom checkbox</label>
</div>
Bootstrap 5 (simplified checkbox):
<div class="form-check">
<input type="checkbox" class="form-check-input" id="check5">
<label class="form-check-label" for="check5">Check this checkbox</label>
</div>
The v5 example is shorter – no more custom-control wrapper or special input class name. Yet, it will be styled as a nicely customized checkbox (with a styled box and checkmark) in both cases. In v4, if you used the default checkbox (without custom-control), it would have looked like the browser’s default. In v5, the default is the custom style. So essentially, Bootstrap 5 collapses the distinction. When migrating, you’d remove those extra wrapper classes and just use the new ones. The output visually will be equivalent or better.
Floating Label Example:
Bootstrap 4 did not have floating labels, so one might have used a third-party plugin or custom CSS. In Bootstrap 5, it’s built-in. Here’s how you would create a floating label in v5:
<div class="form-floating">
<input type="text" class="form-control" id="username" placeholder="Username">
<label for="username">Username</label>
</div>
There is no direct v4 equivalent to show, but to highlight: the placeholder attribute is required (it can be an empty string) to reserve space, and then the label is placed after the input. With the .form-floating class, that label will float when the field is focused or has text. If you tried this markup in v4, nothing special would happen, but in v5 this just works. It’s a new pattern made easy.
Accordion Markup Differences:
This is a more extensive example. Let’s say you want an accordion (collapsible content sections).
In Bootstrap 4, you might have done:
<div id="accordionExample">
<div class="card">
<div class="card-header" id="headingOne">
<h2 class="mb-0">
<button class="btn btn-link" data-toggle="collapse" data-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
Collapsible Group Item #1
</button>
</h2>
</div>
<div id="collapseOne" class="collapse show" aria-labelledby="headingOne" data-parent="#accordionExample">
<div class="card-body">
Some placeholder content for the first accordion panel.
</div>
</div>
</div>
<!-- more .card for additional items... -->
</div>
This uses the card component to give a bordered look, and ties the collapse to the card via IDs and data-parent.
In Bootstrap 5, the same accordion would be:
<div class="accordion" id="accordionExample">
<div class="accordion-item">
<h2 class="accordion-header" id="headingOne">
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">
Accordion Item #1
</button>
</h2>
<div id="collapseOne" class="accordion-collapse collapse show" aria-labelledby="headingOne" data-bs-parent="#accordionExample">
<div class="accordion-body">
Some placeholder content for the first accordion panel.
</div>
</div>
</div>
<!-- more .accordion-item for additional items... -->
</div>
What differences do we see?
.accordion in v5 (versus just an id in v4)..accordion-item that wraps each section (instead of .card)..accordion-header and the toggle button has .accordion-button class (no need for .btn-link or custom styling)..accordion-collapse and the content has .accordion-body (instead of .card-body).data-toggle became data-bs-toggle, and data-target → data-bs-target, and data-parent → data-bs-parent as expected.The new markup is more semantic and easier to distinguish as an accordion in the code. Functionally, both do the same thing: they allow only one panel open at a time (thanks to the parent attribute) and toggle content. But the v5 approach means less custom CSS (the card component isn’t hijacked for something it wasn’t originally intended for). If you migrate an accordion from v4 to v5, you’ll be reworking the HTML structure to this new format. It’s a bit of effort, but the Bootstrap 5 accordion is more robust (for example, it includes built-in iconography and better alignment out of the box).
These examples cover some of the common things you’ll encounter. There are, of course, many other small changes (for instance, the classes for responsive embeds changed to .ratio classes, utility class names for flex alignment changed slightly, etc.), but the above should give a representative taste of the Bootstrap 4 vs 5 differences in code.
When in doubt, referring to Bootstrap 5’s documentation for the component or feature in question is the best way to see the new syntax and then adjust your code accordingly. And remember, a lot of Bootstrap 4 code will actually work or at least not break badly even if not immediately changed (for example, a .btn-block class in your HTML will just be ignored in Bootstrap 5, so the button will just behave normally instead of full-width, which might not be catastrophic). But to fully leverage Bootstrap 5, you’ll want to make all the necessary adjustments as shown above.
Give Vroni a GitHub issue, bug report, spec, or rough idea. It reads the repo, plans the change, writes code, runs checks, and works toward a review-ready pull request.
Take a look at vroni.com