ASP.NET Core 5.0 - Migrate To Bootstrap v5

Ken Haggerty
Created 06/10/2021 - Updated 09/14/2021 02:57

This article will describe how to migrate an ASP.NET Core 5.0 Razor Pages project from Bootstrap v4 to v5. I will assume you have downloaded the ASP.NET Core 5.0 - Users Without Passwords Project or created a new ASP.NET Core 5.0 Razor Pages project. See Tutorial: Get started with Razor Pages in ASP.NET Core.

Users Without Passwords Project and Article Series

I have developed two separate projects in the Users Without Passwords Project (UWPP) solution. The Users Without Passwords v4 project supports Bootstrap v4 and the new Users Without Passwords project supports Bootstrap v5. The new version is published at Fido.KenHaggerty.Com. You can register a new user with Windows Hello or a FIDO2 security key. Details, screenshots, and related articles can be found at ASP.NET Core 5.0 - Users Without Passwords Project. The details page includes the version change log.

Update 09/14/2021

I updated the Twitter Bootstrap CDN link and integrity hash to version 5.1.1.

I have migrated KenHaggerty.Com to Bootstrap v5. KenHaggerty.Com implements Native JavaScript for Bootstrap. Bootstrap's bootstrap.bundle.min.js @ v5.0.1 is 76.9 KB (78,748 bytes). Bootstrap Native's bootstrap-native.min.js @ v4.0.0 is 38.7 KB (39,729 bytes). Since v4.0.0, bootstrap-native.js supports Bootstrap v5 and bootstrap-native-v4.js supports Bootstrap v4. I have developed two separate projects in the Users Without Passwords Project (UWPP). The Users Without Passwords v4 project supports Bootstrap v4 and the new Users Without Passwords project supports Bootstrap v5.

KenHaggerty.Com and the Users Without Passwords projects implement the Cloudflare CDN service for bootstrap.min.css and bootstrap-native.min.js. For the integrity attribute to work correctly, the local file must be exactly the same as the CDN version. Update the project's libman.json to match current versions. See ASP.NET Core 2.2 - Manage Client-Side Libraries.

Bootstrap v5 libman.json:
{
  "version": "1.0",
  "defaultProvider": "cdnjs",
  "libraries": [
    {
      "destination": "wwwroot/lib/bootstrap/dist/",
      "files": [
        "css/bootstrap.css",
        "css/bootstrap.css.map",
        "css/bootstrap.min.css",
        "css/bootstrap.min.css.map"
      ],
      "library": "twitter-bootstrap@5.1.1"
    },
    {
      "destination": "wwwroot/lib/bootstrap/dist/js/",
      "files": [
        "bootstrap-native.esm.js",
        "bootstrap-native.esm.min.js",
        "bootstrap-native.js",
        "bootstrap-native.min.js",
        "polyfill.js",
        "polyfill.min.js"
      ],
      "library": "bootstrap.native@4.0.6"
    }
  ]
}
Bootstrap v5 Pages > Shared > _Layout.cshtml:
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.1.1/css/bootstrap.min.css"
        asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css" asp-suppress-fallback-integrity="true"
        asp-fallback-test-class="visually-hidden" asp-fallback-test-property="position" asp-fallback-test-value="absolute"
        integrity="sha512-6KY5s6UI5J7SVYuZB4S/CZMyPylqyyNZco376NM2Z8Sb8OxEdp02e1jkKk/wZxIEmjQ6DRCEBhni+gpr9c4tvA=="
        crossorigin="anonymous" referrerpolicy="no-referrer" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap.native/4.0.6/bootstrap-native.min.js"
        asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap-native.min.js" asp-fallback-test="window.BSN"
        integrity="sha512-os4qxPq5Rf7xni4mEhIUr9TwzB+ta+8kGm7wt0H6fYcLRyVsB5111QiB341xtld35GsDFVU78k1SAswDghR2yQ=="
        crossorigin="anonymous" referrerpolicy="no-referrer">
</script>

Notice the asp-fallback-test-class for bootstrap.min.css is visually-hidden rather than sr-only. This article attempts to add value to many articles and blogs about migrating to Bootstrap v5. I found Abi Rana's blog Cheatsheet on converting Bootstrap v4 project to Bootstrap v5 which helped to quickly mitigate most issues. I enhance and expand his Find and Replace list for the new class names. The list addresses most classes but not all. You should review Bootstrap v5.0 - Migrating to v5.

“Screen reader” classes are now “visually hidden” classes. Bootstrap v5.0 - Migrating to v5 - Helpers

Find Replace With
sr-only visually-hidden

Horizontal direction specific variables, utilities, and mixins have all been renamed to use logical properties like those found in flexbox layouts—e.g., start and end in lieu of left and right. Bootstrap v5.0 - Migrating to v5 - RTL Renamed several utilities to use logical property names instead of directional names with the addition of RTL support: Bootstrap v5.0 - Migrating to v5 - Utilities

Find Replace With
left- start-
right- end-
-left -start
-right -end
ml- ms-
mr- me-
pl- ps-
pr- pe-

Dropped form-specific layout classes for our grid system. Use our grid and utilities instead of .form-group, .form-row, or .form-inline. Bootstrap v5.0 - Migrating to v5 - Forms

Find Replace With
form-group mb-3
form-row row

Added .fs-* utilities for font-size utilities (with RFS enabled). These use the same scale as HTML’s default headings (1-6, large to small), and can be modified via Sass map. Bootstrap v5.0 - Migrating to v5 - Utilities

Find Replace With
font-weight- fw-
font-style- fst-

Miscellaneous from Abi Rana's blog Cheatsheet on converting Bootstrap v4 project to Bootstrap v5

Find Replace With
no-gutters g-0
embed-responsive ratio
embed-responsive-16by9 ratio-16x9
badge-primary bg-primary
badge-pill rounded-pill
rounded-lg & rounded-sm rounded-1, rounded-2 & rounded-3
btn-block d-block w-100 (as alternative)

Dropped native .form-control-file and .form-control-range. Dropped .input-group-append and .input-group-prepend. You can now just add buttons and .input-group-text as direct children of the input groups. .custom-file and .form-file have been replaced by custom styles on top of .form-control. Dropped form-specific layout classes for our grid system. Use our grid and utilities instead of .form-group, .form-row, or .form-inline. Bootstrap v5.0 - Migrating to v5 - Forms

Find Replace With
form-control-file As Required
form-control-range As Required
input-group-append As Required
input-group-prepend As Required
form-inline As Required
custom-file As Required
form-file As Required

Consolidated native and custom form elements. Checkboxes, radios, selects, and other inputs that had native and custom classes in v4 have been consolidated. Now nearly all our form elements are entirely custom, most without the need for custom HTML. Bootstrap v5.0 - Migrating to v5 - Forms

Find Replace With
custom-check form-check
custom-switch form-switch
custom-select form-select
custom-range form-range

KenHaggerty.Com and the Users Without Passwords projects implement the new checkbox and radio classes. See Bootstrap-Razor-Model Checkboxes and Radio Button Groups.

Ensure the input element for all checkboxes and radios have the type attribute set. (type="checkbox" or type="radio")
Find Replace With
custom-control custom-checkbox form-check
custom-control custom-radio form-check
custom-control-inline form-check-inline
custom-control-input form-check-input
custom-control-label form-check-label
Bootstrap v4 Checkbox:
<div class="custom-control custom-checkbox custom-control-inline">
    <input class="custom-control-input" id="V4CheckboxId" />
    <label class="custom-control-label" for="V4CheckboxId">
        V4 Checkbox
    </label>
</div>
Bootstrap v5 Checkboxes:
<div class="col-12 mb-2">
    <div class="form-check form-check-inline">
        <input type="checkbox" class="form-check-input" id="CheckboxSelectAll" />
        <label class="form-check-label" for="CheckboxSelectAll">
            Select All
        </label>
    </div>
    <div class="form-check form-check-inline">
        <input type="checkbox" class="form-check-input demo" checked id="V5DebugCheckboxId" />
        <label class="form-check-label" for="V5DebugCheckboxId">
            Debug
        </label>
    </div>
    <div class="form-check form-check-inline">
        <input type="checkbox" class="form-check-input demo" id="V5InformationCheckboxId" />
        <label class="form-check-label" for="V5InformationCheckboxId">
            Information
        </label>
    </div>
    <div class="form-check form-check-inline">
        <input type="checkbox" class="form-check-input demo" checked id="V5WarningCheckboxId" />
        <label class="form-check-label" for="V5WarningCheckboxId">
            Warning
        </label>
    </div>
    <div class="form-check form-check-inline">
        <input type="checkbox" class="form-check-input demo" id="V5ErrorCheckboxId" />
        <label class="form-check-label" for="V5ErrorCheckboxId">
            Error
        </label>
    </div>
    <div class="form-check form-check-inline">
        <input type="checkbox" class="form-check-input demo" id="V5CriticalCheckboxId" />
        <label class="form-check-label" for="V5CriticalCheckboxId">
            Critical
        </label>
    </div>
</div>
Bootstrap v4 Radio Button:
<div class="custom-control custom-radio custom-control-inline">
    <input type="radio" class="custom-control-input" name="V4RadioButtonGroup" value="Debug" checked id="DebugV4RadioButtonId" />
    <label class="custom-control-label" for="DebugV4RadioButtonId">
        Debug
    </label>
</div>
Bootstrap v5 Radio Button Group:
<div class="col-12">
    <div class="form-check form-check-inline">
        <input type="radio" class="form-check-input" name="V5RadioButtonGroup" value="Debug" checked id="DebugV5RadioButtonId" />
        <label class="form-check-label" for="DebugV5RadioButtonId">
            Debug
        </label>
    </div>
    <div class="form-check form-check-inline">
        <input type="radio" class="form-check-input" name="V5RadioButtonGroup" value="Information" id="InformationV5RadioButtonId" />
        <label class="form-check-label" for="InformationV5RadioButtonId">
            Information
        </label>
    </div>
    <div class="form-check form-check-inline">
        <input type="radio" class="form-check-input" name="V5RadioButtonGroup" value="Warning" id="WarningV5RadioButtonId" />
        <label class="form-check-label" for="WarningV5RadioButtonId">
            Warning
        </label>
    </div>
    <div class="form-check form-check-inline">
        <input type="radio" class="form-check-input" name="V5RadioButtonGroup" value="Error" id="ErrorV5RadioButtonId" />
        <label class="form-check-label" for="ErrorV5RadioButtonId">
            Error
        </label>
    </div>
    <div class="form-check form-check-inline">
        <input type="radio" class="form-check-input" name="V5RadioButtonGroup" value="Critical" id="CriticalV5RadioButtonId" />
        <label class="form-check-label" for="CriticalV5RadioButtonId">
            Critical
        </label>
    </div>
</div>

Form labels now require .form-label. Bootstrap v5.0 - Migrating to v5 - Forms

Find All Ensure
<label form-label OR form-check-label

Select elements require the .form-select class.

Find All Ensure
<select form-select
Select without form-select class.
Select with form-select class.

Renamed .close to .btn-close for a less generic name. Bootstrap v5.0 - Migrating to v5 - Close button

Find Replace With
close btn-close

Close buttons now use a background-image (embedded SVG) instead of a &times; in the HTML, allowing for easier customization without the need to touch your markup. Bootstrap v5.0 - Migrating to v5 - Close button

Find Remove
&times; Parent span

Data attributes for all JavaScript plugins are now namespaced to help distinguish Bootstrap functionality from third parties and your own code. For example, we use data-bs-toggle instead of data-toggle. Bootstrap v5.0 - Migrating to v5 - JavaScript

Find Replace With
data-dismiss data-bs-dismiss
data-ride data-bs-ride
data-interval data-bs-interval
data-slide data-bs-slide
data-target data-bs-target
data-toggle data-bs-toggle

KenHaggerty.Com and the Users Without Passwords projects implement message-modal.js which dynamically creates a modal. The dataset attributes with the bs namespace required updating. For example, messageDismissButton. dataset. dismiss = 'modal'; needs to be messageDismissButton. dataset. bsDismiss = 'modal';.

Bootstrap v4 Modal
<!-- Button trigger modal -->
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal">
  Launch demo modal
</button>

<!-- Modal -->
<div class="modal" tabindex="-1">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title">Modal title</h5>
        <button type="button" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body">
        <p>Modal body text goes here.</p>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
        <button type="button" class="btn btn-primary">Save changes</button>
      </div>
    </div>
  </div>
</div>
Bootstrap v5 Modal
<!-- Button trigger modal -->
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#exampleModal">
  Launch demo modal
</button>

<!-- Modal -->
<div class="modal" tabindex="-1">
  <div class="modal-dialog top-5">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title">Modal title</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
      </div>
      <div class="modal-body">
        <p>Modal body text goes here.</p>
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
        <button type="button" class="btn btn-primary">Save changes</button>
      </div>
    </div>
  </div>
</div>

KenHaggerty.Com overrides the modal-footer class to center buttons with a declaration in site.css.

.modal-footer {
    justify-content: center;
}

The Users Without Passwords v4 project implements a modal-dialog.top-50 and a modal-dialog.top-100 with declarations in site.css.

.modal-dialog.top-50 {
    top: 50px;
}
.modal-dialog.top-100 {
    top: 100px;
}

Bootstrap v5 implements a top-50 and a top-100 which override the declarations in site.css.

.top-0 {
  top: 0 !important;
}
.top-50 {
  top: 50% !important;
}
.top-100 {
  top: 100% !important;
}
.bottom-0 {
  bottom: 0 !important;
}
.bottom-50 {
  bottom: 50% !important;
}
.bottom-100 {
  bottom: 100% !important;
}
.start-0 {
  left: 0 !important;
}
.start-50 {
  left: 50% !important;
}
.start-100 {
  left: 100% !important;
}
.end-0 {
  right: 0 !important;
}
.end-50 {
  right: 50% !important;
}
.end-100 {
  right: 100% !important;
}

KenHaggerty.Com and the Users Without Passwords projects implement a new top-5 class for modal-dialog classes with a declaration in site.css.

.modal-dialog.top-5 {
    top: 5%;
}

KenHaggerty.Com and the Users Without Passwords projects implement Bootstrap css root variables. The variable declaration added the bs namespace. For example, --bs-primary: #0d6efd; instead of --primary: #007bff;.

Find Replace With
var(-- var(--bs-
Bootstrap v5 root variable
<style>
    circle {
        fill: var(--bs-primary);
    }
</style>

Dropped .text-justify class. Bootstrap v5.0 - Migrating to v5 - Content, Reboot, etc
See GitHub Pull - #29793 and GitHub Issues - #29679

Find Remove
text-justify As Required

KenHaggerty.Com and the Users Without Passwords projects implement the text-justify class for p tags with a declaration in site.css.

p.text-justify {
    font-size: 1.2rem;
    text-align: justify;
}
Bootstrap v5 New breakpoint!

Added new xxl breakpoint for 1400px and up. No changes to all other breakpoints. Bootstrap v5.0 - Migrating to v5 - Grid updates

1920X1080 - Users Without Passwords Project v4 Home Page
1920X1080 - Bootstrap v4
1920X1080 - Users Without Passwords Project v5 Home Page
1920X1080 - Bootstrap v5

Comment Count = 0

Please log in to comment.

Login Register
Logged in users receive web notifications.
Web Notifications