Skip to main content

📐 Bootstrap Grid System Mastery

The grid is the single most useful thing Bootstrap gives you: a flexbox-powered, twelve-column layout engine that adapts from a phone to a widescreen monitor with a handful of classes. This lesson takes you from the three core building blocks to auto-layout, ordering, offsets, nesting, and the modern gutters API.

🎯 Learning Objectives

By the end of this lesson, you will be able to:

  • Explain the roles of containers, rows, and columns and how they nest
  • Use the 12-column model and the six responsive breakpoints to build layouts that reflow across devices
  • Apply auto-layout, row-cols-*, column ordering, and offsets to control placement
  • Control spacing with the Bootstrap 5 gutters API (g-*, gx-*, gy-*)
  • Diagnose the most common grid mistakes and follow the do/don't rules for maintainable markup

Estimated Time: 35–45 minutes  •  Difficulty: Intermediate

Hands-on: Build a responsive product grid that shows 1, 2, 3, then 4 cards per row as the screen widens.

In This Lesson

Why a Grid at All?

Before CSS grid and flexbox were widely supported, laying out a page meant fighting with floats, clearfixes, and hard-coded pixel widths. Bootstrap's grid packaged the hard parts into a small vocabulary of classes so you can describe a layout — "this is a third of the width on desktop, full width on mobile" — and let the framework do the math.

💡 Analogy — city planning: Think of the grid as a city plan. The container is the city limits, setting the outer boundary. Rows are the streets that organize space horizontally. Columns are the building lots, each allocated a share of the block. The planner's rule — twelve lots per block — guarantees everything lines up no matter how big the city grows.

The grid you'll learn here is Bootstrap 5, which is built on flexbox. It has evolved a long way from the float-based grids of earlier versions:

flowchart LR A["Bootstrap 3
float-based
xs·sm·md·lg"] --> B["Bootstrap 4
flexbox
+ xl, auto-layout"] B --> C["Bootstrap 5
flexbox
+ xxl, gutters API"]

Bootstrap 5's grid gives you six responsive breakpoints, flexbox alignment, auto-layout columns, a dedicated gutters API for spacing, plus ordering and offsetting — all without writing custom CSS.

Anatomy: Container, Row, Column

Every grid layout is built from three nested pieces. The order is strict: columns live inside rows, and rows live inside containers.

Container wrapping a row wrapping columns A container box holds a row box, and the row holds three equal column boxes side by side. .container .row .col .col .col
Figure 1 — Containers wrap rows, rows wrap columns. Breaking this nesting order is the source of most grid bugs.

1. Containers

A container is the outermost wrapper. It centers your content and applies the responsive horizontal padding (half a gutter on each side). There are three flavors:

ClassBehaviorUse when
.containerFixed max-width that steps down at each breakpointStandard centered page content (most common)
.container-fluidAlways 100% wideFull-bleed layouts, app shells, dashboards
.container-{sm…xxl}100% wide until the named breakpoint, then fixedFluid on small screens, bounded on large ones
<!-- Fixed-width, centered -->
<div class="container">...</div>

<!-- Always full width -->
<div class="container-fluid">...</div>

<!-- 100% wide until the md breakpoint, then fixed -->
<div class="container-md">...</div>

2. Rows

A row is a flex container for columns. It uses negative horizontal margins to cancel the container's padding so column edges align flush with the container edge. Always put a row inside a container (or another column).

3. Columns

Columns are the immediate children of a row and hold your actual content. You size them with the col-* classes described next.

📖 Key Terms

Gutter: the horizontal (and, in Bootstrap 5, vertical) space between columns, created by column padding.

Breakpoint: a screen-width threshold (e.g. 768px) at which the layout is allowed to change.

Auto-layout: letting Bootstrap divide the row equally among columns instead of naming a width.

The 12-Column System

Each row is divided into 12 equal units. A col-* class says how many of those units a column claims. The number 12 was chosen because it divides cleanly by 1, 2, 3, 4, 6, and 12 — so halves, thirds, and quarters all come out evenly.

LayoutClassesEach column's share
Two equalcol-6 + col-650% each
Three equalcol-4 × 333.3% each
Four equalcol-3 × 425% each
Sidebar + maincol-3 + col-925% / 75%
<!-- Two equal columns -->
<div class="row">
  <div class="col-6">Column 1</div>
  <div class="col-6">Column 2</div>
</div>

<!-- Sidebar (25%) + main content (75%) -->
<div class="row">
  <div class="col-3">Sidebar</div>
  <div class="col-9">Main content</div>
</div>
Two twelve-unit rows split into columns The first row shows two equal col-6 blocks; the second shows a narrow col-3 beside a wide col-9. col-6 + col-6 col-6 col-6 col-3 + col-9 col-3 col-9
Figure 2 — Column numbers always add up to 12 within a row. If they exceed 12, the extra columns wrap to a new line.

⚠️ Watch the total

If the column numbers in a row add up to more than 12, the overflowing columns wrap onto the next line. That's sometimes intentional (a self-wrapping gallery) and sometimes a bug — always know which you meant.

Responsive Breakpoints

The real power comes from breakpoint-prefixed classes like col-md-6. A prefix says "apply this width from this screen width and up." Bootstrap is mobile-first: a class with no prefix applies to every size, and each prefix only adds overrides for larger screens.

BreakpointPrefixApplies fromTypical device
Extra smallcol- (none)0pxPhones (portrait)
Smallcol-sm-≥ 576pxPhones (landscape)
Mediumcol-md-≥ 768pxTablets
Largecol-lg-≥ 992pxLaptops
Extra largecol-xl-≥ 1200pxDesktops
XXLcol-xxl-≥ 1400pxLarge desktops

Stack several prefixes on one element to describe how a column reflows as the screen grows:

<div class="row">
  <!--
    full width on phones,
    half width from sm up,
    one third from md up,
    one quarter from lg up
  -->
  <div class="col-12 col-sm-6 col-md-4 col-lg-3">Item 1</div>
  <div class="col-12 col-sm-6 col-md-4 col-lg-3">Item 2</div>
  <div class="col-12 col-sm-6 col-md-4 col-lg-3">Item 3</div>
  <div class="col-12 col-sm-6 col-md-4 col-lg-3">Item 4</div>
</div>

✅ Mobile-first, in one sentence

Style the smallest screen first with an unprefixed class, then add prefixed classes only where a larger screen needs something different. You never write a "make it smaller for mobile" rule — mobile is the default.

Auto-layout, Order & Offsets

Auto-layout columns

Drop the number entirely and use bare col. Bootstrap splits the row equally among however many col elements it finds — add or remove one and the rest resize automatically.

<!-- Three equal columns, no arithmetic needed -->
<div class="row">
  <div class="col">Equal</div>
  <div class="col">Equal</div>
  <div class="col">Equal</div>
</div>

<!-- One fixed column, the rest share what's left -->
<div class="row">
  <div class="col-4">Fixed 4/12</div>
  <div class="col">Auto</div>
  <div class="col">Auto</div>
</div>

The row-cols-* classes set how many equal columns appear per row at each breakpoint — ideal for card galleries:

<!-- 1 per row on phones, 2 on md, 3 on lg, 4 on xl -->
<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 row-cols-xl-4 g-4">
  <div class="col">Card 1</div>
  <div class="col">Card 2</div>
  <div class="col">Card 3</div>
  <div class="col">Card 4</div>
</div>

Ordering

The order-* classes change the visual order without touching the HTML source order — great for putting a sidebar first visually on desktop while keeping the main content first in the markup for accessibility and SEO.

<div class="row">
  <!-- Main content: first in HTML (good for screen readers),
       but shown second (on the right) on lg screens -->
  <div class="col-12 col-lg-8 order-lg-2">Main content</div>
  <!-- Sidebar: second in HTML, shown first (on the left) on lg -->
  <div class="col-12 col-lg-4 order-lg-1">Sidebar</div>
</div>

Offsets

The offset-* classes push a column to the right by inserting empty units before it — handy for centering or indenting content.

<div class="row">
  <div class="col-md-4">Left</div>
  <!-- 4 empty units, then the column -->
  <div class="col-md-4 offset-md-4">Pushed right</div>
</div>
Offset column layout A col-md-4 block on the left, a dashed empty offset-md-4 gap in the middle, and a col-md-4 block on the right. col-md-4 offset-md-4 col-md-4
Figure 3 — An offset reserves empty units before a column. Here offset-md-4 leaves a four-unit gap.

Nesting & the Gutters API

Nesting

You can put a new row inside a column to subdivide it. The nested row starts its own fresh 12-unit grid scoped to the parent column's width.

<div class="row">
  <div class="col-sm-6">
    <h3>Main column</h3>
    <div class="row">               <!-- nested grid, 12 units of THIS column -->
      <div class="col-6">Nested A</div>
      <div class="col-6">Nested B</div>
    </div>
  </div>
  <div class="col-sm-6">Other main column</div>
</div>

The gutters API

In Bootstrap 5, spacing between columns is controlled by g-* classes on the row, on a 0–5 scale. Use gx-* for horizontal-only and gy-* for vertical-only gutters — vertical gutters are what add breathing room between wrapped card rows.

<div class="row g-0">...</div>     <!-- no gutters (edge to edge) -->
<div class="row g-2">...</div>     <!-- small gutters -->
<div class="row gx-5 gy-3">...</div> <!-- wide horizontal, medium vertical -->

💡 Why gutters moved to the row

In Bootstrap 4 you fought gutters with negative margins and padding overrides. Bootstrap 5 drives them through the --bs-gutter-x / --bs-gutter-y CSS variables that the g-* classes set, so spacing is now declarative and consistent across the row.

Hands-on: Product Grid

🏋️ Build a responsive product grid

Objective: Create a grid of product cards that shows 1 card per row on phones, 2 on small, 3 on medium, and 4 on large screens, with even spacing and equal-height cards.

Starter (load Bootstrap 5 from the CDN in your page head):

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">

Instructions:

  1. Wrap everything in a .container.
  2. Create a row that uses row-cols-* to change how many cards appear per breakpoint.
  3. Add a gutter class so the cards have space between them.
  4. Give each card h-100 so every card in a row matches the tallest.
  5. Resize your browser (or use DevTools device mode) and watch the count change.
💡 Hint

The per-row count lives on the row, not the columns: row-cols-1 row-cols-sm-2 row-cols-md-3 row-cols-lg-4. Each card is just a bare <div class="col">. Add g-4 to the same row for spacing, and h-100 on each .card for equal height.

✅ Solution
<div class="container py-4">
  <h2 class="text-center mb-4">Featured Products</h2>

  <div class="row row-cols-1 row-cols-sm-2 row-cols-md-3 row-cols-lg-4 g-4">
    <div class="col">
      <div class="card h-100">
        <div class="card-body">
          <h5 class="card-title">Product One</h5>
          <p class="card-text">$19.99</p>
          <p class="card-text">A short product description.</p>
          <button class="btn btn-primary mt-auto">Add to cart</button>
        </div>
      </div>
    </div>

    <!-- Repeat the .col block for each product -->
    <div class="col">
      <div class="card h-100">
        <div class="card-body">
          <h5 class="card-title">Product Two</h5>
          <p class="card-text">$24.99</p>
          <p class="card-text">Another short description.</p>
          <button class="btn btn-primary mt-auto">Add to cart</button>
        </div>
      </div>
    </div>
  </div>
</div>

Because the count is declared once on the row, you can drop in as many .col cards as you like and they all follow the same responsive rules.

🎯 Quick Quiz

Question 1: Which nesting order is correct in the Bootstrap grid?

Question 2: A column has the classes col-12 col-md-6. How wide is it on a 500px-wide phone?

Question 3: In Bootstrap 5, which class adds vertical spacing between wrapped rows of cards?

Common Mistakes & Best Practices

✅ Do🚫 Don't
Put columns directly inside a rowPlace a col straight inside a container with no row
Put rows inside a container (or a column)Nest a container inside another container
Nest a new row inside a column to subdivideNest a row directly inside a row without a wrapping column
Design mobile-first, then add larger breakpointsBuild desktop-first and bolt mobile on afterward
Use g-* gutters for spacingAdd ad-hoc margins to columns to fake spacing

Mistake: a row directly inside a row

<!-- WRONG: nested row is not wrapped in a column -->
<div class="row">
  <div class="row">
    <div class="col-6">A</div>
    <div class="col-6">B</div>
  </div>
</div>

<!-- RIGHT: wrap the nested row in a column -->
<div class="row">
  <div class="col-12">
    <div class="row">
      <div class="col-6">A</div>
      <div class="col-6">B</div>
    </div>
  </div>
</div>

Mistake: columns with no row

<!-- WRONG: columns are not inside a row -->
<div class="container">
  <div class="col-6">A</div>
  <div class="col-6">B</div>
</div>

<!-- RIGHT -->
<div class="container">
  <div class="row">
    <div class="col-6">A</div>
    <div class="col-6">B</div>
  </div>
</div>

⚠️ Keep the grid semantic

Grid classes are for layout, not meaning. Rather than putting col-md-9 straight on a <main>, wrap it: <div class="col-md-9"><main>…</main></div>. Your document keeps clean landmark elements and the layout stays flexible.

Summary & Quiz

🎉 Key Takeaways

  • The grid nests strictly: container → row → column.
  • Every row is 12 units; col-* classes claim a share and must total 12 or they wrap.
  • Bootstrap is mobile-first — unprefixed classes apply everywhere; prefixes add rules for larger screens.
  • Use auto-layout (col), row-cols, order, and offsets to place content without custom CSS.
  • Spacing is the gutters API: g-*, gx-*, gy-* on the row.

📚 Further Reading

🚀 What's Next?

With layout under control, the next lesson turns to the pieces you'll place inside the grid — Bootstrap Components and Utilities: navbars, cards, modals, forms, and the utility classes that let you style them without writing CSS.

🎉 Nicely done!

You can now reason about any Bootstrap layout by asking three questions: which container, how many units per column, and at which breakpoint does it change.