Bootstrap 4 to 5 Migration: Critical CSS Breaking Changes That Destroy Layouts

Migrating from Bootstrap 4 to 5 requires replacing directional utilities like ml-* with ms-*, updating grid systems to use CSS gap instead of negative margins, and removing deprecated classes like .btn-block and .custom-*, or your entire application styling will collapse.

Upgrading a production application from Bootstrap 4 to Bootstrap 5 introduces fundamental changes to the CSS framework that can cause complete UI failure if not addressed systematically. While the Tailwind CSS team has developed sophisticated codemods in tailwindlabs/tailwindcss to handle similar utility-class migrations—specifically the migrateLegacyClasses function in packages/@tailwindcss-upgrade/src/codemods/template/migrate-legacy-classes.ts—Bootstrap 5 requires manual auditing of several critical breaking changes that affect layout, spacing, and component styling.

Utility Class Renames in Bootstrap 4 to 5 Migration

The most immediate breaking change during a bootstrap 4 to 5 migration is the comprehensive renaming of directional utility classes. Bootstrap 5 replaced physical direction names (left/right) with logical properties (start/end) to improve RTL (right-to-left) language support.

Directional Spacing Utilities

The margin and padding utilities changed their prefixes entirely:

  • ml-* (margin-left) → ms-* (margin-start)
  • mr-* (margin-right) → me-* (margin-end)
  • pl-* (padding-left) → ps-* (padding-start)
  • pr-* (padding-right) → pe-* (padding-end)

Since these utilities control spacing throughout most layouts, their removal causes immediate visual collapse. Elements using ml-3 or mr-auto will lose their spacing entirely because these class names are no longer generated in Bootstrap 5's CSS output.

Float and Text Alignment Updates

Float and text alignment utilities also adopted the start/end naming convention:

  • float-left → float-start
  • float-right → float-end
  • text-left → text-start
  • text-right → text-end

Applications relying on these classes for navigation bars, image positioning, or text alignment will experience visual regressions until these classes are updated.

Grid System Overhaul: From Negative Margins to CSS Gap

Bootstrap 5 fundamentally changed how grid gutters work, which can break custom grid implementations that relied on the previous behavior.

Row Gutter Implementation

In Bootstrap 4, .row elements used negative margins to create gutters between columns. Bootstrap 5 replaces this with the CSS gap property. While this is more modern and easier to override, existing custom CSS that targeted .row margins or padding may now clash with the gap-based layout, causing misaligned columns.

If your application uses custom grid overrides that relied on negative margins or specific padding calculations, you must update them to respect the new gap model or explicitly disable gutters using g-0.

Column Class Simplification

The grid tier class names were simplified, and the xs breakpoint was effectively removed (it now starts at null and applies to all breakpoints). Custom media queries that assumed specific Bootstrap 4 breakpoint behaviors may shift unexpectedly at viewport widths of 576px, 992px, and other thresholds, causing responsive layout jumps.

Form Control and Component Removals

Bootstrap 5 removed several component-specific classes and rewrote form controls entirely, which can cause forms to appear unstyled or broken.

Removal of Custom Form Classes

The .custom-* class namespace was removed entirely. You must replace these classes with their new equivalents:

  • .custom-select → .form-select
  • .custom-file → .form-control (with type="file")
  • .custom-range → .form-range

These new classes use native form controls with updated base styling. Existing forms using the old .custom-control wrappers will lose their styling, spacing, and focus indicators, appearing as default browser inputs.

Button Layout Breaking Changes

The .btn-block class was removed entirely. Buttons that previously stretched full-width by using .btn-block will revert to inline size, breaking layouts that depended on full-width buttons.

To achieve the same effect in Bootstrap 5, you must use utility classes like .w-100 or wrapper elements with .d-grid and .gap-2.

Typography and Iconography Adjustments

Typography utilities underwent significant changes that affect text density and hierarchy. The .lead class now has a different line-height, which may cause text blocks to appear too dense. Additionally, the display heading utilities (.display-1 through .display-4) were removed from the base classes and replaced with font-size utilities (.fs-1, .fs-2, etc.).

Applications relying on display classes for hero sections or large typography will need to update their class names or manually adjust font sizes and line heights.

Bootstrap 5 also dropped the built-in SVG icon set. The bi icon class prefix changed, and icons will disappear entirely if the Bootstrap Icons library is not explicitly included.

Automating the Migration with Codemods

While Bootstrap does not provide an official codemod, you can automate class name replacements using regex-based scripts similar to those found in tailwindlabs/tailwindcss. The migrateLegacyClasses function in packages/@tailwindcss-upgrade/src/codemods/template/migrate-legacy-classes.ts demonstrates how to map legacy utilities while preserving responsive variants, and the safety checks in packages/@tailwindcss-upgrade/src/codemods/template/is-safe-migration.test.ts ensure migrations do not unintentionally change user-defined values.

The following TypeScript utility, inspired by the Tailwind migration logic in packages/@tailwindcss-upgrade/src/index.ts, automatically rewrites CSS files to convert Bootstrap 4 directional utilities to their Bootstrap 5 equivalents:

// utils/bootstrap5-migration.ts
import fs from 'node:fs';
import path from 'node:path';

// Mapping of old utilities → new utilities
const MAP = new Map<string, string>([
  ['ml-', 'ms-'],
  ['mr-', 'me-'],
  ['pl-', 'ps-'],
  ['pr-', 'pe-'],
  ['float-left', 'float-start'],
  ['float-right', 'float-end'],
  ['text-left', 'text-start'],
  ['text-right', 'text-end'],
]);

export function migrateBootstrap4(filePath: string) {
  const src = fs.readFileSync(filePath, 'utf8');
  let transformed = src;

  for (const [oldPrefix, newPrefix] of MAP) {
    // Handle numbered utilities like ml-3, mr-auto, etc.
    const regex = new RegExp(`\\b${oldPrefix}(\\w+)\\b`, 'g');
    transformed = transformed.replace(regex, `${newPrefix}$1`);
  }

  fs.writeFileSync(filePath, transformed);
  console.log(`Migrated ${filePath}`);
}

// Example usage:
// migrateBootstrap4(path.resolve('src/styles/main.css'));

This script implements the same defensive migration strategy found in packages/@tailwindcss-upgrade/src/codemods/template/migrate-legacy-classes.ts, ensuring that only exact class name matches are replaced. For version detection logic to guard against running migrations on wrong versions, reference the pattern in packages/@tailwindcss-upgrade/src/utils/version.ts.

Summary

  • Directional utilities renamed: ml-* becomes ms-*, mr-* becomes me-*, and similar changes affect padding, float, and text alignment classes.
  • Grid system uses CSS gap: Negative margin hacks on .row elements no longer work; gutters now use the gap property.
  • Custom form classes removed: .custom-select, .custom-file, and .custom-range are replaced with .form-select, .form-control, and .form-range.
  • Button blocks eliminated: .btn-block is removed; use .w-100 or grid utilities instead.
  • Automation possible: Adapt codemod patterns from tailwindlabs/tailwindcss, specifically migrateLegacyClasses in packages/@tailwindcss-upgrade/src/codemods/template/migrate-legacy-classes.ts, to automate class replacements.

Frequently Asked Questions

What is the most common breaking change in Bootstrap 4 to 5 migration?

The most common breaking change is the renaming of directional utility classes from left/right to start/end. Classes like ml-3 (margin-left) and mr-auto (margin-right) no longer exist in Bootstrap 5; they have been replaced with ms-* (margin-start) and me-* (margin-end). Since these utilities control spacing throughout most layouts, their removal causes immediate visual collapse across the entire application.

How do I migrate custom form controls to Bootstrap 5?

Bootstrap 5 removed the entire .custom-* namespace for form controls. You must replace .custom-select with .form-select, .custom-file with .form-control (using type="file"), and .custom-range with .form-range. These new classes use native form elements with updated base styling, so you should also audit any custom CSS that targeted the old wrapper structures, as the DOM hierarchy and class names have changed significantly.

Are Bootstrap 4 and 5 compatible?

Bootstrap 4 and 5 are not compatible in the same project. You cannot mix Bootstrap 4 and 5 classes because Bootstrap 5 uses different CSS custom properties, changed breakpoint behaviors, and removed or renamed numerous utility classes. Attempting to use both versions simultaneously will result in conflicting styles, broken layouts, and unpredictable component behavior. A complete migration of all templates and stylesheets is required.

How can I automate the Bootstrap 4 to 5 migration?

While Bootstrap does not provide an official codemod, you can automate class name replacements using regex-based scripts similar to those in tailwindlabs/tailwindcss. The migrateLegacyClasses function in packages/@tailwindcss-upgrade/src/codemods/template/migrate-legacy-classes.ts demonstrates how to map legacy utilities while preserving responsive variants. You can adapt this pattern to create a Node.js script that scans your HTML and CSS for Bootstrap 4 classes (like ml-* or float-left) and rewrites them to their Bootstrap 5 equivalents (ms-*, float-start), though you should always verify the output with visual regression testing.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →