There is no direct v4 to v6 path. Migrate in two stages — v4 to v5 first (this skill), then v5 to v6 (the
bootstrap-v5-v6-migration
skill). Skipping the middle stage doesn't work, because many v4 names were renamed in v5 and renamed
again in v6:
becomes
only after passing through v5, and
becomes
by way of
.
Work through each step in order. After each step, search the codebase for remaining v4 patterns before moving on.
-
Update the dependency and drop the jQuery/Popper v1 pair:
sh
npm i --save bootstrap@5 @popperjs/core
npm uninstall popper.js
(v1) is replaced by
(v2). Bootstrap 5's bundle includes Popper; the standalone
does not.
-
Switch the Sass compiler to Dart Sass (
). Node Sass / LibSass is not supported. Keep using
for now — v5 still uses it; the move to
happens in the v6 stage.
-
Sass maps no longer merge automatically. In v4, adding one key to
was enough; in v5 you must define the complete map, including the keys you're keeping.
-
Update renamed functions and mixins:
| v4 | v5 |
|---|
| |
| , | Removed — reference / directly |
| / |
| / | / |
media-breakpoint-down(md)
| media-breakpoint-down(lg)
— takes the breakpoint itself, not the next one down |
media-breakpoint-between(sm, md)
| media-breakpoint-between(sm, lg)
— same shift on the upper bound |
The breakpoint parameter change is silent: nothing errors, your media queries just apply one breakpoint off. Audit every call.
-
Print styles,
flags, and the utility API all changed shape in v5. If you generated custom utilities with v4 mixins, rewrite them through v5's
map.
-
Namespace every data attribute with
. This is a mechanical but wide-reaching change:
Any option passed as an attribute follows the same rule:
becomes
.
-
Replace jQuery plugin calls with constructors or static getters:
js
// v4
$('#myModal').modal('show')
$('#myTooltip').tooltip()
// v5
const modal = new bootstrap.Modal(document.querySelector('#myModal'))
modal.show()
bootstrap.Tooltip.getOrCreateInstance(document.querySelector('#myTooltip'))
-
Replace jQuery event handlers with native listeners. Bootstrap 5 dispatches native
s, so
$(el).on('shown.bs.modal', …)
stops firing:
js
// v5
document.querySelector('#myModal').addEventListener('shown.bs.modal', event => {
// event.relatedTarget replaces event.relatedTarget from the jQuery event
})
-
Smaller API renames:
- → , and prefer the new .
- The sanitizer option → .
- Tooltip and Popover / still accept functions, but is no longer the jQuery element.
-
If you relied on jQuery's
or on
-style calls, rewrite them against the class API — there is no jQuery interface left to shim.
The responsive variants follow the same rename (
→
).
This is the largest v4 to v5 rewrite: custom form controls were merged into the standard classes.
Input groups lose their wrappers: delete
and
and place
, buttons, and controls as direct children of
. Validation feedback moves out of the removed wrappers too.
Badges also lost their link styling — a
no longer gets hover/focus states.
Once the project builds and renders on Bootstrap 5, switch to the
bootstrap-v5-v6-migration
skill and work through its steps. Do not attempt those renames while v4 patterns are still present — its find-and-replace steps assume v5 names.
Two v4-era details worth flagging before you start it: the
→
element you produced in Step 4 keeps that name in v6 (but must stay empty, with no child SVG), and the
markup you just wrote gets rebuilt again into v6's
/
/
structure.