Cards
A card is a bordered box that groups related content: a product with its photo and price, a course with its duration, a team member with a short bio. Cards are probably the most used Bootstrap component, because they look good alone and even better in a grid.
In this lesson you will build cards from their parts, arrange them in responsive grids with equal heights, and make horizontal and fully clickable cards.
The parts of a card
A card is a div with the card class. Inside it, you combine the parts you need:
| Class | Role |
|---|---|
card-body | The padded main area. Most content goes here |
card-title, card-subtitle | Heading and smaller sub-heading |
card-text | A paragraph (removes the bottom margin of the last one) |
card-img-top, card-img-bottom | An image flush with the top or bottom edge, with matching rounded corners |
card-header, card-footer | Shaded strips above and below the body |
card-link | Links placed side by side |
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Card parts</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container py-3">
<div class="row g-3">
<div class="col-md-6">
<div class="card">
<img src="https://course.ethicscomputer.in/assets/samples/landscape-400.svg" class="card-img-top" alt="Hills near Munnar" width="400" height="250">
<div class="card-body">
<h2 class="card-title h5">Munnar Weekend Trip</h2>
<h3 class="card-subtitle h6 mb-2 text-body-secondary">2 nights, from Kochi</h3>
<p class="card-text">Tea gardens, Eravikulam National Park and a sunrise at Top Station. Hotel and transport included.</p>
<a href="#book" class="btn btn-primary">Book for ₹6,499</a>
</div>
</div>
</div>
<div class="col-md-6">
<div class="card">
<div class="card-header">Today's batch</div>
<div class="card-body">
<h2 class="card-title h5">Python for Beginners</h2>
<p class="card-text">Rohtak centre, 6 pm to 8 pm. Twelve seats left.</p>
<a href="#syllabus" class="card-link">Syllabus</a>
<a href="#trainer" class="card-link">Trainer</a>
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item">Duration: 8 weeks</li>
<li class="list-group-item">Mode: Classroom</li>
</ul>
<div class="card-footer text-body-secondary">Starts Monday</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>The second card contains a list group: list-group on a <ul> and list-group-item on each <li>. Adding list-group-flush removes its outer borders and rounded corners so it sits neatly inside the card. List groups are also useful on their own, for menus and settings lists; add list-group-item-action to items that are links or buttons to get a hover effect.
Cards have no fixed width. They fill their parent, so control their size with grid columns or sizing classes, never with a width on the card itself.
Card grids with equal heights
Put each card in a grid column and use row-cols-* to set how many per row. One problem appears at once: cards with less text are shorter than their neighbours. Add h-100 to every card so it fills the full height of its column, and the row looks tidy.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Card grid</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container py-3">
<h1 class="h4 mb-3">Kirana Fresh: today's offers</h1>
<div class="row row-cols-1 row-cols-sm-2 row-cols-lg-3 g-3">
<div class="col">
<div class="card h-100">
<div class="card-body">
<h2 class="card-title h6">Basmati rice, 5 kg</h2>
<p class="card-text">Aged long-grain rice.</p>
</div>
<div class="card-footer d-flex justify-content-between align-items-center">
<span class="fw-bold">₹549</span>
<button type="button" class="btn btn-sm btn-success">Add</button>
</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<div class="card-body">
<h2 class="card-title h6">Toor dal, 1 kg</h2>
<p class="card-text">Unpolished, from Maharashtra farms. Cooks soft in 3 whistles and keeps its flavour.</p>
</div>
<div class="card-footer d-flex justify-content-between align-items-center">
<span class="fw-bold">₹168</span>
<button type="button" class="btn btn-sm btn-success">Add</button>
</div>
</div>
</div>
<div class="col">
<div class="card h-100 border-warning">
<div class="card-body">
<span class="badge text-bg-warning mb-2">Only 4 left</span>
<h2 class="card-title h6">Cold-pressed groundnut oil, 1 L</h2>
<p class="card-text">Wood-pressed, no chemicals.</p>
</div>
<div class="card-footer d-flex justify-content-between align-items-center">
<span class="fw-bold">₹289</span>
<button type="button" class="btn btn-sm btn-success">Add</button>
</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>Because each card is a flex column, the card-footer always sits at the bottom, so the prices and buttons line up across the row even when the descriptions have different lengths. Colour a card with border-* (as the oil card does), with text-bg-* for a solid background, or with bg-*-subtle for a soft tint.
If you want cards glued together with shared borders and no gaps, wrap them in a card-group instead of a grid row. It is less flexible, but good for a three-plan pricing strip.
Horizontal cards
For a list of results, an image on the left and text on the right reads better. Use a grid inside the card, with g-0 to remove the gutters:
<div class="card mb-3">
<div class="row g-0">
<div class="col-md-4">
<img src="https://course.ethicscomputer.in/assets/samples/portrait.svg" class="img-fluid rounded-start" alt="Book cover">
</div>
<div class="col-md-8">
<div class="card-body">
<h2 class="card-title h5">Wings of Fire</h2>
<p class="card-text">Autobiography of Dr A. P. J. Abdul Kalam. Paperback, ₹299.</p>
<p class="card-text"><small class="text-body-secondary">Ships from Lucknow in 2 days</small></p>
</div>
</div>
</div>
</div>On phones the image stacks above the text; from md they sit side by side. rounded-start rounds only the image's left corners to match the card.
Making the whole card clickable
Users often try to click anywhere on a product card. Add stretched-link to the main link inside the card: an invisible layer stretches the link over the whole card, which already has position: relative.
<div class="card">
<div class="card-body">
<h2 class="card-title h5">Rajasthani thali</h2>
<p class="card-text">Dal baati churma, gatte, ker sangri. ₹320.</p>
<a href="/menu/thali" class="stretched-link">View dish</a>
</div>
</div>Use only one stretched link per card. Other buttons inside the card will be covered by it unless you give them position-relative.
Image overlays
card-img-overlay places the body on top of the image, like a poster. Make sure the text is readable over the image: use dark images with text-white, or a dark semi-transparent background on the text.
<div class="card text-white">
<img src="https://course.ethicscomputer.in/assets/samples/landscape.svg" class="card-img" alt="Sunset over the hills">
<div class="card-img-overlay d-flex align-items-end">
<h2 class="card-title h4 bg-dark bg-opacity-50 p-2 rounded">Diwali sale: up to 40% off</h2>
</div>
</div>Common mistakes
- Setting a width on the card. The layout breaks on phones. Let the grid column control the width.
- Uneven card heights in a grid. Add
h-100to each card, and put actions in acard-footerso they line up. - Several
stretched-links in one card. Only the last one works across the card; others are hidden under it. - Images without
card-img-top. A plain image keeps square corners that stick out of the rounded card.
Key points
- A card is
cardplus parts such ascard-body,card-title,card-text,card-img-top,card-headerandcard-footer. - Cards fill their container; size them with grid columns.
row row-cols-* g-*plush-100on each card makes a responsive, equal-height card grid.- A row with
g-0inside a card creates a horizontal card. stretched-linkmakes the whole card clickable, andlist-group-flushputs a borderless list inside a card.
Exercise
Build a "Meet our trainers" section with three trainer cards (Ananya, Kabir and Isha). Each card should have the avatar.svg image on top, the trainer's name as the title, their subject as a subtitle, one sentence about them, and a footer showing their experience. On phones show one card per row, from md three per row, all the same height. Make each whole card clickable with a link to #trainer-name.
Show solution
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Our trainers</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container py-3">
<div class="row row-cols-1 row-cols-md-3 g-4">
<div class="col">
<div class="card h-100">
<img src="https://course.ethicscomputer.in/assets/samples/avatar.svg" class="card-img-top" alt="Ananya" width="200" height="200">
<div class="card-body">
<h2 class="card-title h5"><a href="#ananya" class="stretched-link text-decoration-none">Ananya</a></h2>
<h3 class="card-subtitle h6 mb-2 text-body-secondary">Web design</h3>
<p class="card-text">Teaches HTML, CSS and Bootstrap with real client projects.</p>
</div>
<div class="card-footer text-body-secondary">8 years of experience</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<img src="https://course.ethicscomputer.in/assets/samples/avatar.svg" class="card-img-top" alt="Kabir" width="200" height="200">
<div class="card-body">
<h2 class="card-title h5"><a href="#kabir" class="stretched-link text-decoration-none">Kabir</a></h2>
<h3 class="card-subtitle h6 mb-2 text-body-secondary">Python</h3>
<p class="card-text">Loves turning boring office tasks into small scripts.</p>
</div>
<div class="card-footer text-body-secondary">5 years of experience</div>
</div>
</div>
<div class="col">
<div class="card h-100">
<img src="https://course.ethicscomputer.in/assets/samples/avatar.svg" class="card-img-top" alt="Isha" width="200" height="200">
<div class="card-body">
<h2 class="card-title h5"><a href="#isha" class="stretched-link text-decoration-none">Isha</a></h2>
<h3 class="card-subtitle h6 mb-2 text-body-secondary">Databases</h3>
<p class="card-text">Explains SQL joins using cricket scorecards.</p>
</div>
<div class="card-footer text-body-secondary">6 years of experience</div>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>row-cols-1 row-cols-md-3 g-4 builds the responsive grid, h-100 equalises the heights, and the name link with stretched-link covers each whole card.
Quick Quiz
1. Which class should a card image use so its top corners match the card's rounded corners?
card-img-top makes the image full width and rounds its top corners to fit the card.
2. Cards in a grid row have different heights. What fixes it?
h-100 makes each card fill its column's height, and all columns in a row are equally tall.
3. What does stretched-link do?
It adds an invisible layer so a click anywhere on the card follows that link.
4. Which Bootstrap 4 class was removed in Bootstrap 5?
card-deck and card-columns were removed; use the grid with row-cols-* instead.
Learn Bootstrap with a mentor
Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
