Components

Bootstrap 5 Cards: Layouts, Images, Grids, and Examples

By the bootstrap.build team · 9 min read · Published

A Bootstrap card is a bordered box for one piece of content: a product, a blog post, a team member, a pricing plan. It is a div with the .card class, and inside it you combine an image, a body with a title and text, a header, a footer, or a list. This guide covers every part of the Bootstrap 5.3 card component with copy-paste examples, then the layouts people actually need: responsive card grids, cards of equal height with buttons lined up at the bottom, horizontal cards, and clickable cards.

A basic card

<div class="card" style="width: 18rem;">
  <img src="photo.jpg" class="card-img-top" alt="A short description of the photo">
  <div class="card-body">
    <h5 class="card-title">Card title</h5>
    <h6 class="card-subtitle mb-2 text-body-secondary">Card subtitle</h6>
    <p class="card-text">A short description that sits under the title.</p>
    <a href="/details/" class="btn btn-primary">Read more</a>
  </div>
</div>

The parts:

Class What it does
.card The container: border, rounded corners, and a flex column
.card-body The padded area for content
.card-title The heading, on an <h*> element
.card-subtitle A secondary heading, usually with mb-2 text-body-secondary
.card-text Paragraph text
.card-link Links placed side by side
.card-img-top, .card-img-bottom An image with corners rounded to match the card
.card-header, .card-footer Shaded strips at the top and bottom

Cards have no fixed width and no margin. They fill the width of their parent, so you size and space them with the grid or utilities, covered below.

Images

Put an image above or below the body with .card-img-top or .card-img-bottom:

<div class="card">
  <img src="photo.jpg" class="card-img-top" alt="...">
  <div class="card-body">
    <p class="card-text">Text under the image.</p>
  </div>
</div>

Images of different sizes. In a row of cards, photos with different proportions make the cards uneven. Give the images a fixed height and crop them with object-fit-cover:

<img src="photo.jpg" class="card-img-top object-fit-cover" style="height: 200px;" alt="...">

Or keep a fixed shape with the ratio helper (more image techniques are in the Bootstrap images guide):

<div class="ratio ratio-16x9">
  <img src="photo.jpg" class="card-img-top object-fit-cover" alt="...">
</div>

Text over the image. Use .card-img on the image and put the content in .card-img-overlay:

<div class="card text-bg-dark">
  <img src="photo.jpg" class="card-img" alt="...">
  <div class="card-img-overlay">
    <h5 class="card-title">Title over the image</h5>
    <p class="card-text">Keep overlay text short, or it spills past the image.</p>
  </div>
</div>

Check the contrast: white text on a light photo is unreadable. A dark image, or a semi-transparent dark background behind the text, keeps it legible.

<div class="card text-center">
  <div class="card-header">Featured</div>
  <div class="card-body">
    <h5 class="card-title">Special offer</h5>
    <p class="card-text">Details of the offer.</p>
    <a href="/offer/" class="btn btn-primary">See the offer</a>
  </div>
  <div class="card-footer text-body-secondary">Ends in 2 days</div>
</div>

A list group inside a card makes a tidy list of features or links. Add .list-group-flush so it sits edge to edge without its own border:

<div class="card" style="width: 18rem;">
  <div class="card-header">Plan includes</div>
  <ul class="list-group list-group-flush">
    <li class="list-group-item">10 projects</li>
    <li class="list-group-item">Custom domain</li>
    <li class="list-group-item">Email support</li>
  </ul>
</div>

For tabs in the header, add .card-header-tabs to a .nav-tabs list (or .card-header-pills to .nav-pills):

<div class="card">
  <div class="card-header">
    <ul class="nav nav-tabs card-header-tabs">
      <li class="nav-item"><a class="nav-link active" aria-current="true" href="#">Overview</a></li>
      <li class="nav-item"><a class="nav-link" href="#">Specs</a></li>
    </ul>
  </div>
  <div class="card-body">...</div>
</div>

Card colors and borders

Since Bootstrap 5.2, text-bg-{color} sets a background with a readable text color in one class:

<div class="card text-bg-primary mb-3">
  <div class="card-header">Header</div>
  <div class="card-body">
    <h5 class="card-title">Primary card</h5>
    <p class="card-text">Text color is chosen for contrast automatically.</p>
  </div>
</div>

Every theme color works: text-bg-secondary, text-bg-success, text-bg-danger, text-bg-warning, text-bg-info, text-bg-light, and text-bg-dark.

For an outline style, change only the border, and match the header and footer:

<div class="card border-success mb-3">
  <div class="card-header bg-transparent border-success">Header</div>
  <div class="card-body text-success">...</div>
  <div class="card-footer bg-transparent border-success">Footer</div>
</div>

Add depth with the shadow utilities, shadow-sm, shadow, or shadow-lg, and remove the border with border-0 for a flatter look. Every border, corner, and shadow class is listed in the borders and shadows guide. Color should never be the only way you show meaning, such as a red card for an error; say it in the text too.

Card sizes

Cards are 100% wide by default. Three ways to size them:

<!-- With the grid (recommended) -->
<div class="row">
  <div class="col-sm-6"><div class="card">...</div></div>
  <div class="col-sm-6"><div class="card">...</div></div>
</div>

<!-- With width utilities -->
<div class="card w-50">...</div>

<!-- With a fixed width -->
<div class="card" style="width: 18rem;">...</div>

The grid is usually the right choice, because the cards then resize with the screen. Text alignment works on the whole card or any part: text-center or text-end.

Responsive card grids

The most common card layout is a grid that shows one card per row on phones and more on wider screens. Use row-cols-* on the row and put each card in a .col:

<div class="row row-cols-1 row-cols-md-2 row-cols-lg-3 g-4">
  <div class="col">
    <div class="card h-100">
      <img src="one.jpg" class="card-img-top" alt="...">
      <div class="card-body">
        <h5 class="card-title">First card</h5>
        <p class="card-text">Short text.</p>
      </div>
    </div>
  </div>
  <div class="col">
    <div class="card h-100">...</div>
  </div>
  <div class="col">
    <div class="card h-100">...</div>
  </div>
</div>

That gives one column on phones, two from 768px (md), and three from 992px (lg); see Bootstrap breakpoints for the widths. g-4 sets the gap between cards in both directions, and h-100 makes every card in a row the same height. The grid system guide explains columns and gutters in depth.

Card grids are the building blocks of pricing sections and dashboards: see them in a full page in the Bootstrap landing page template and Bootstrap admin template guides.

Equal height cards with buttons at the bottom

h-100 makes cards the same height, but if their text is different lengths, the buttons still end up at different heights. Push the button to the bottom with flexbox:

<div class="row row-cols-1 row-cols-md-3 g-4">
  <div class="col">
    <div class="card h-100">
      <div class="card-body d-flex flex-column">
        <h5 class="card-title">Starter</h5>
        <p class="card-text">A short description.</p>
        <a href="/starter/" class="btn btn-primary mt-auto">Choose Starter</a>
      </div>
    </div>
  </div>
  <!-- more cards -->
</div>

d-flex flex-column turns the body into a column, and mt-auto on the button takes up the spare space above it, so every button sits on the same line. A .card-footer does the same job without the extra classes, since it always sits at the bottom of an h-100 card. To make every card full height by default, set $card-height: 100% in Sass.

Card groups

.card-group joins cards into one unit with shared borders, equal widths, and equal heights, from the sm breakpoint up:

<div class="card-group">
  <div class="card">...</div>
  <div class="card">...</div>
  <div class="card">...</div>
</div>

Card groups suit a short set of related cards, like three plans. For anything that should wrap onto more rows, use a grid with row-cols instead. Bootstrap 4’s .card-deck was removed in Bootstrap 5; a grid with row-cols and g-* gutters replaces it.

Horizontal cards

For an image beside the text, put a grid row inside the card and remove its gutters with g-0:

<div class="card mb-3" style="max-width: 540px;">
  <div class="row g-0">
    <div class="col-md-4">
      <img src="photo.jpg" class="img-fluid rounded-start h-100 object-fit-cover" alt="...">
    </div>
    <div class="col-md-8">
      <div class="card-body">
        <h5 class="card-title">Horizontal card</h5>
        <p class="card-text">The image sits on the left from 768px up and above the text on phones.</p>
      </div>
    </div>
  </div>
</div>

rounded-start rounds only the image’s left corners to match the card.

Clickable cards

To make the whole card a link without wrapping it in an <a>, add .stretched-link to the link inside it:

<div class="card">
  <img src="post.jpg" class="card-img-top" alt="...">
  <div class="card-body">
    <h5 class="card-title">How to start a blog</h5>
    <p class="card-text">A short summary of the post.</p>
    <a href="/blog/start-a-blog/" class="stretched-link">Read the post</a>
  </div>
</div>

The link’s clickable area stretches over the card, because .card is already positioned. Keep only one link per card when you use it, since other links inside become unclickable. A subtle hover effect helps people see the card is clickable. Add a class such as card-hover to those cards:

.card-hover { transition: transform .15s ease, box-shadow .15s ease; }
.card-hover:hover { transform: translateY(-2px); box-shadow: var(--bs-box-shadow); }

Loading placeholders

While content loads, show a placeholder card with Bootstrap’s placeholder classes:

<div class="card" aria-hidden="true">
  <div class="card-body">
    <h5 class="card-title placeholder-glow">
      <span class="placeholder col-6"></span>
    </h5>
    <p class="card-text placeholder-glow">
      <span class="placeholder col-7"></span>
      <span class="placeholder col-4"></span>
      <span class="placeholder col-6"></span>
    </p>
    <a class="btn btn-primary disabled placeholder col-6" aria-disabled="true"></a>
  </div>
</div>

Masonry layouts

Bootstrap 5 has no built-in masonry layout, where cards of different heights pack together like bricks. The old CSS-only version from Bootstrap 4 was dropped. Use the Masonry JavaScript plugin with a normal card grid, as Bootstrap’s own masonry example does, or CSS columns (column-count) for a simple version that orders cards top to bottom.

Customizing cards

Every card reads its spacing, colors, and borders from CSS variables, so you can restyle cards in plain CSS:

.card {
  --bs-card-border-radius: 1rem;
  --bs-card-border-color: transparent;
  --bs-card-spacer-y: 1.5rem;
  --bs-card-spacer-x: 1.5rem;
  --bs-card-cap-bg: transparent;
}

Or set the Sass variables before importing Bootstrap:

Variable Controls Default
$card-spacer-y, $card-spacer-x Body padding $spacer
$card-title-spacer-y Space under the title $spacer * .5
$card-border-width Border width var(--bs-border-width)
$card-border-color Border color var(--bs-border-color-translucent)
$card-border-radius Corner radius var(--bs-border-radius)
$card-box-shadow Shadow null
$card-cap-bg Header and footer background A 3% tint of the text color
$card-bg Card background var(--bs-body-bg)
$card-height Card height null (set 100% for equal heights)
$card-group-margin Gap in card groups Half the grid gutter

The Bootstrap Sass variables guide explains how overrides work, and the bootstrap.build theme builder lets you change card padding, borders, and radius visually and export the CSS.

Common card problems

Cards touch each other. Cards have no margin. In a grid, add a gutter class such as g-4 to the row; for stacked cards, add mb-3 to each.

Cards in a row have different heights. Add h-100 to each card, and use d-flex flex-column with mt-auto to line up the buttons.

Images are different heights. Give them a fixed height with object-fit-cover, or wrap them in a ratio helper.

The layout breaks on phones. Don’t give cards a fixed pixel width inside a grid. Let the columns size them, and start with row-cols-1 for phones.

card-deck does nothing. It was removed in Bootstrap 5. Use row row-cols-* g-* instead.

FAQ

How do I make a card in Bootstrap?

Create a div with the card class and put a div with the card-body class inside it, containing a card-title heading and card-text paragraph. Add an image with card-img-top above the body, and a card-header or card-footer if you need them.

How do I make Bootstrap cards the same height?

Put the cards in a grid row and add the h-100 class to each card, so they fill the height of their column. To line up buttons at the bottom, add d-flex flex-column to the card-body and mt-auto to the button.

How do I display Bootstrap cards in a row?

Use a grid row with row-cols classes, such as <div class="row row-cols-1 row-cols-md-3 g-4">, and wrap each card in a <div class="col">. That shows one card per row on phones and three from the md breakpoint up, with g-4 setting the gap.

How do I make a responsive card grid in Bootstrap 5?

Add row-cols-* classes for each breakpoint to the row, for example row-cols-1 row-cols-sm-2 row-cols-lg-4, and put each card in a .col. The number of cards per row then changes with the screen width without any custom CSS.

What replaced card-deck in Bootstrap 5?

The grid replaced it. Use a row with row-cols-* classes and a gutter class such as g-4, and add h-100 to the cards for equal heights. card-group still exists for cards that should join together with shared borders.

How do I make a whole Bootstrap card clickable?

Add the stretched-link class to a link inside the card. Its clickable area then covers the whole card, because the card is already positioned. Use only one link per card with this method.

How do I make a horizontal card in Bootstrap?

Put a <div class="row g-0"> inside the card, with the image in a col-md-4 column and the card-body in a col-md-8 column. Add rounded-start to the image so its corners match the card.

Try it in the builder

Make these changes visually with a live preview, then export clean Bootstrap Sass or CSS.

Open the Builder