What Are Bootstrap Cards?
The card is Bootstrap’s general-purpose content box. One bordered container holds an image, text, list items, links and a footer, and because the component runs on flexbox, alignment and stacking work without extra CSS.
According to the official documentation, a card in Bootstrap 5.3.8 sets no width, so it fills its parent, and sets no margin, so spacing utilities handle the gaps. It is also position: relative, which is what makes stretched links work.
Cards pair easily with other Bootstrap components. A list group or a button goes straight into a card with no wrapper tricks.
Bootstrap 3 had panels and wells for this job, plus thumbnails on the side. Cards replaced all of them, and the old looks survive as modifier classes (Bootstrap 5.3 documentation).
Bootstrap’s version of the card layout pattern gives each item its own box with a clear edge.
A card holds content. It does not place itself on the page, because rows, columns and card groups do that.
Card Markup and the Classes That Build It
Only two classes are required, card on the wrapper and card-body on the padded content inside it. Titles, text, images, headers and footers each get their own class, but you can skip any of them.
Building a basic card
- Create a div with the class card.
- Add an img with card-img-top at the top of the wrapper if the card needs a picture, and write real alt text for it.
- Add a div with the class card-body below the image.
- Inside the body, place a heading with card-title and a paragraph with card-text.
- Close with a button, or with a link that carries card-link.
The result is plain HTML with class names, so a card needs no JavaScript.
Titles, subtitles and links
card-title goes on any heading tag, and the docs examples use h5. The class sets the look, so pick whichever heading level the page outline needs.
card-subtitle sits under the title inside card-body and lines up with it. The docs add mb-2 and text-body-secondary to it.
Anchor tags get card-link, and several of them can sit side by side.
Headers and footers
card-header and card-footer are direct children of the card, outside the body. Put them inside the body and they sit within its padding instead of spanning the full width.
A heading tag can carry card-header on its own, so an h5 with that class works as a header without a wrapper div.
Content Types Inside a Card: List Groups, Nav Tabs, Links and Buttons
Most of what you would want inside a card (list groups, navs, links, buttons) drops in directly, provided it goes in the right slot.
List groups
Add list-group and list-group-flush to a ul, and the list becomes its own block inside the card. It can stand alone without a card-body. It also works under a card-header or above a card-footer.
The kitchen sink example in the docs goes further. A flush list group sits between two card-body blocks, with an image above and links below.
Navigation in the header
For tabs, put a ul with nav, nav-tabs and card-header-tabs inside card-header. Pills use the same structure with nav-pills and card-header-pills.
The card example shows markup only. Switching content between panes takes the tab plugin, and this walkthrough of Bootstrap tabs covers that part.
Links and clickable cards
Links on a card come in a few forms, and they differ in how much of the card responds to a click.
| Element | Appearance | Click area |
|---|
| card-link | Plain text link | The link text |
| btn btn-primary | Filled button | The button |
| Either, plus stretched-link | Unchanged | The whole card |
Button variants such as btn-primary are covered in this guide to Bootstrap buttons.
stretched-link works through a ::after pseudo-element that covers the nearest positioned ancestor. Cards are position: relative already, so the class needs no extra HTML.
It stops working in a couple of cases. Bootstrap’s stretched link page recommends against multiple links and tap targets in one stretched block. An inner element with a position other than static, a transform, a perspective or, in Firefox, a filter also becomes the new containing block, which shrinks the clickable area to that element.
Image Placement: Top, Bottom, Overlay and Horizontal Cards
Images can sit on top of a card, at the bottom, behind the text as an overlay, or beside it in a horizontal layout.
Top and bottom image caps
card-img-top and card-img-bottom round the matching corners to fit the card’s border radius. For a top cap the img comes first inside the card. For a bottom cap it goes after the card-body.
Image overlays
Put card-img on the image to turn it into the card background, then place the text in a div with card-img-overlay. The docs example adds text-bg-dark to the card, which gives light text on a dark image.
Overlay content must not be taller than the image, or it spills outside the picture.
Text sits on whatever pixels the photo holds. Test its color contrast against the brightest part of the image, not the average.
Horizontal cards
Start with a card that has a max-width, set to 540px in the docs example. Inside it goes a row with row g-0, which removes the gutters. The image column is col-md-4 and holds an img with img-fluid and rounded-start, and the text column is col-md-8 and holds the card-body.
Below the md breakpoint (768px) the columns stack, so the image sits above the text on phones. The docs warn that further adjustments may be needed depending on the content.
Bootstrap 5 also dropped the media object component, since flexbox utilities can recreate it. The horizontal card above uses the grid instead, a pattern that already existed in Bootstrap 4.
How to Size Cards and Arrange Them in a Responsive Grid
A card is as wide as its parent until you set a width, and the grid decides how many cards share a row.
Sizing a single card
| Method | Example | Use when |
|---|
| Grid columns | col-sm-6 around the card | The card belongs to a page grid |
| Sizing utilities | w-75, w-50 | A quick percentage width is enough |
| Custom CSS | width: 18rem | The width must stay fixed |
When the page already runs on the Bootstrap grid system, put the card inside a col-* column and let the column set the width.
The docs example adds mb-3 mb-sm-0 to the first column. That gives the cards a gap when stacked and none once they sit side by side.
Rows of cards with row-cols and gutters
row-cols-1 row-cols-md-2 g-4 on the row gives one card per row on small screens and two from the md breakpoint up.
Bootstrap breakpoints are built on min-width media queries, so a md class applies at 768px and at every wider size.
Wrap each card in a div with the class col, and set the card count per breakpoint on the row. With row-cols-3 and four cards, the fourth wraps to a new line. $grid-row-columns defaults to 6, and any row-cols value above that limit is ignored.
The default gutter is 1.5rem, and g-4 produces the same value. Use gx-* for horizontal gaps, gy-* for vertical ones and g-0 to remove both.
Equal height with h-100
The columns in a row stretch to equal height, but the card inside each column only grows when told to. Add h-100 to every card and a short card matches its taller neighbors.
Footers line up automatically once h-100 is on the cards. To make equal height the default across a project, set $card-height to 100% in Sass (Bootstrap 5.3 documentation).
Card Group, Row-Cols or Masonry: Which Layout Fits?
Pick card-group for a joined strip of a few cards, row-cols with h-100 for separate cards in a grid, and Masonry when card heights differ too much for equal rows.
| Layout | Look | Height | Breaks when |
|---|
| card-group | One attached strip | Equal by default | You need gaps between cards |
| row-cols with h-100 | Separate cards with gutters | Equal per row | h-100 is missing, so heights follow content |
| Masonry plugin | Uneven columns packed tight | Natural per card | The script fails to load, or h-100 forces equal heights |
If the content lengths are similar and you want visible gaps, row-cols with h-100 is the safe pick. Two to four cards that should read as one unit suit card-group. Quotes, photos and text cards mixed into one grid is what Masonry is for, and I would not bring it in for anything less.
card-group starts stacked and switches to display: flex at the sm breakpoint (576px), per the Bootstrap 5.3 docs. Footers line up across the attached cards without extra classes.
Masonry is not bundled with Bootstrap. The official 5.3 example loads masonry-layout 4.2.2 from the jsDelivr CDN and adds a data-masonry attribute with percentPosition set to true on the row.
Leave h-100 off in a Masonry grid. Equal heights cancel the reason to use it.
Bootstrap 4 had a CSS-only answer called card-columns. It filled cards down each column instead of across rows, and it needed display: inline-block to stop cards splitting between columns (Bootstrap 4.6 documentation).
Teams that would rather skip the plugin can look at CSS-only masonry layouts, though Bootstrap does not ship one.
One oddity in the docs: the 5.3 card page still says these layout options are not yet responsive, and the same sentence appears in the 4.6 docs. The examples right below it do respond, since card-group attaches from sm and row-cols-md-* applies from md.
Styling Bootstrap Cards With Utilities and CSS Variables
Utilities handle one-off changes to a single card. The –bs-card-* CSS variables are the better route when every card should change.
Utilities for color and borders
text-bg-{color} sets a background and a contrasting text color in one class. Bootstrap added it in v5.2.0, and the older pairing of text-* and bg-* still works.
border-{color} changes the border color only. Add a text-{color} class to the card or its body if the text should match.
Headers and footers take their own utilities. bg-transparent removes the cap tint, and a border-{color} class recolors its edge (Bootstrap 5.3 documentation).
CSS variables on the card
Since v5.2.0, every card carries local variables set on .card, so one class can restyle a single card or all of them.
| Goal | Variable |
|---|
| Rounder corners | –bs-card-border-radius |
| Card shadow | –bs-card-box-shadow, plus box-shadow: var(–bs-card-box-shadow) unless $enable-shadows is on |
| Tinted header and footer | –bs-card-cap-bg |
| Roomier body padding | –bs-card-spacer-x and –bs-card-spacer-y |
Set the variable on a custom class such as product-card, not on .card itself. The rest of the site keeps the defaults.
The shadow variable needs that extra declaration because the card only applies it when $enable-shadows is true in Sass, and the default is false. For a custom shadow value, a box shadow generator saves guesswork before you paste the result into –bs-card-box-shadow.
Bootstrap 5.0 and 5.1 ignore all of these overrides, because the card variables arrived with 5.2.0.
Dark mode works per card too. Setting data-bs-theme=dark on one card flips its palette and leaves the page alone, and cards have carried an explicit text color since 5.3.0-alpha2 so they render correctly across modes.
When Sass is the better route
CSS variables suit projects that load compiled Bootstrap from a CDN, since they need no build step.
Projects that compile Bootstrap anyway can change the Sass variables once, because the docs state that the CSS variable values are set via Sass. The relevant ones are $card-border-radius and $card-spacer-x, plus $card-bg for the background.
Overriding Bootstrap CSS from a separate stylesheet is the fallback when neither route fits, and overriding Bootstrap CSS safely comes down to load order and selector specificity.
Bootstrap Cards in Versions 3, 4 and 5
Cards exist in Bootstrap 4 and 5 only, and the card component itself changed less between them than the layout classes around it did.
| Version | Multi-card layout | Masonry-style columns | Status |
|---|
| Bootstrap 3 | None | None | Final release 3.4.1, Feb 13, 2019 |
| Bootstrap 4 | card-group, card-deck, row-cols | card-columns, CSS only | End of life since Jan 1, 2023, final release 4.6.2, Jul 19, 2022 |
| Bootstrap 5 | card-group, row-cols | Masonry plugin, not bundled | Active, 5.3.8 from Aug 26, 2025 |
Release dates come from the twbs/bootstrap GitHub releases, and the end-of-life status from Bootstrap’s own release support table. As of October 2026, the Bootstrap docs list 5.3.8 as the current release.
Moving a card layout from Bootstrap 4 to 5 mostly means swapping classes, according to the official migration guide and the two versions of the card docs.
- Replace card-deck with a row that carries row-cols-*, and wrap each card in a col.
- Move the gap from mb-4 on each col to a g-* class on the row. The 4.6 grid example uses col mb-4, the 5.3 one uses g-4.
- Swap card-columns for the Masonry plugin.
- Rename no-gutters to g-0 on horizontal cards, and text-right to text-end.
- Replace text-white bg-* pairs with text-bg-*, and text-muted with text-body-secondary (deprecated in 5.3, removal planned for v6).
- Replace any card-based accordion with the Accordion component.
Gutters also shrank. They went from 30px in Bootstrap 4 to 1.5rem (about 24px) in Bootstrap 5, so a ported grid looks slightly tighter.
Sources disagree on the card-deck replacement. Some migration write-ups pair card-deck with card-group, while Bootstrap’s own guide points to the grid.
The two are not interchangeable. The 4.6 docs describe card-deck as cards that are not attached to one another, and card-group attaches them, so swapping in card-group changes the look. Row-cols with gutters keeps the gaps.
When Bootstrap Cards Break or Do Not Fit
Cards run into trouble with low-contrast color variants, loading states that screen readers cannot follow, projects that still need Internet Explorer, and content that another component handles better.
Contrast and color-only meaning
Bootstrap’s accessibility page warns that some default palette combinations fall below the WCAG 2.2 minimums, which are 4.5:1 for text and 3:1 for non-text elements, especially on light backgrounds.
Test every text-bg-* variant you ship, not just the first one. Check the text against its background, then the border against the page. Put the meaning in visible text, because a red border tells a screen reader nothing, and use visually-hidden for a label the design cannot show.
Loading states with placeholders
Bootstrap added placeholders in v5.1, and they work as skeleton screens built from HTML and CSS alone.
To build one, copy the card markup and replace the text with spans that carry placeholder and a col-* width. Add placeholder-glow or placeholder-wave to animate them.
The docs put aria-hidden on the loading card. That hides it from screen readers but announces nothing, so telling users the real content arrived takes extra ARIA attributes and some JavaScript.
Cases where another approach fits better
Internet Explorer 10 and 11 are out, since Bootstrap 5 dropped both. Bootstrap 4 is the last line that supports them, and it reached end of life on January 1, 2023, so it gets no further updates or security patches from the Bootstrap team.
Expand and collapse is another miss. The card-based accordion was replaced by the Accordion component in v5.0, so a card is the wrong tool for toggling content.
A card that holds only a list is overkill too. A list group alone does the same job with less markup.
Custom breakpoint logic is the last one. The Bootstrap docs state that CSS variables do not work in media queries, so a card layout that depends on custom breakpoints needs Sass, not –bs-* variables.
A card holds one self-contained item. When the content is a list, a toggle or a status, a different component fits better.
Bootstrap Cards FAQ
How do I center text in Bootstrap cards?
Add text-center to the card to center every line inside it, or to card-body, card-title or card-text to center one part.
text-end aligns right and text-start aligns left, according to the Bootstrap 5.3 docs.
Can Bootstrap cards have a hover effect?
Yes, but through custom CSS, because the card docs list no hover class. Shadow utilities such as shadow-sm are fixed values and do not react to the pointer.
Put a transition on the card and change its box-shadow or a transform on :hover. Wrap the movement in a prefers-reduced-motion: no-preference media query. These CSS card hover effects show common patterns.
Which CDN link loads Bootstrap for testing card code?
Add the jsDelivr stylesheet for 5.3.8 to the head, at https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css. Keep its integrity attribute and update the hash with each version.
Cards need only the CSS. The JavaScript bundle matters for plugin parts, such as the Tab plugin behind header nav tabs.
Does React-Bootstrap have a Card component?
Yes. React-Bootstrap exports Card with subcomponents such as Card.Body, Card.Title, Card.Text, Card.Img, Card.Header and Card.Footer, and its current docs target Bootstrap 5.
Grids use Row props like xs={1} md={2} with a g-4 class, and each Card sits inside a Col, since card-deck no longer exists.
Where can I find free templates for Bootstrap cards?
Start with the official Bootstrap Examples page. It includes a Pricing example built with cards and an Album template for galleries and portfolios.
Both ship in the 5.3.8 examples download, and Bootstrap’s code is MIT licensed.
The Card Pattern Worth Building First
Bootstrap cards work best when a project starts with one pattern, a row with row-cols-1 row-cols-md-3 g-4, a col around each card, and h-100 on every card.
Grid and equal heights come first, then images, footers and links. Styling through –bs-card-* variables goes last.
Layout leads because images, footers and clickable areas all change card height, and h-100 shows right away whether the row still lines up. Styling can wait because a single custom class changes it without touching the markup.
The cost of h-100 is aligned rows with some empty space inside the shorter cards. I think that is a fair price.
This advice was checked against Bootstrap 5.3.8 in October 2026. The 5.3 migration guide states that CSS variable usage stays incomplete until Bootstrap 6, so the –bs-card-* step is the one to recheck when that release ships.