# React Bootstrap Cards

React Bootstrap card markup as AdminKit uses it: header with title, body, footer, images, list groups, clickable cards and cards rendered from an array.

Cards hold almost every block of the AdminKit dashboards: charts, tables, lists and forms. A card gives each block a frame and a title, so a page full of data still reads as separate pieces.

In [AdminKit for React](/react/) a card is Bootstrap's `.card` markup in JSX. There is no wrapper component. Cards are part of the free and PRO versions.

## Example

The demo pages put the title in the `.card-header` and the content in the `.card-body`:

```html
<div class="card" style="max-width: 24rem;">
  <div class="card-header">
    <h2 class="card-title mb-0">Monthly report</h2>
  </div>
  <div class="card-body">
    <p class="card-text">Revenue grew in every region this month. The full report is ready to download.</p>
    <a href="#" class="btn btn-primary">Download</a>
  </div>
</div>
```
```tsx code
<div className="card" style={{ maxWidth: "24rem" }}>
  <div className="card-header">
    <h2 className="card-title mb-0">Monthly report</h2>
  </div>
  <div className="card-body">
    <p className="card-text">
      Revenue grew in every region this month. The full report is ready to download.
    </p>
    <a href="#" className="btn btn-primary">Download</a>
  </div>
</div>
```

In JSX, `style` takes an object: `style={{ maxWidth: "24rem" }}`. In a page, let the grid size the card instead, with a column such as `<div class="col-12 col-lg-6">`.

## Classes

| Class | Effect |
| --- | --- |
| `card` | The container, with AdminKit's shadow and a bottom margin of one grid gutter |
| `card-header`, `card-body`, `card-footer` | The sections; the header has a 1px bottom border |
| `card-title` | AdminKit: large, bold and muted, whatever the heading level |
| `card-subtitle` | A normal-weight line under the title; the demos use `h6 card-subtitle text-body-secondary` |
| `card-text`, `card-link` | Text and links in the body |
| `card-img-top`, `card-img-bottom` | An image at the top or bottom edge, scaled to the card width |
| `list-group list-group-flush` | A list that runs edge to edge in the card |

AdminKit adds a shadow, a bottom margin and its own `.card-title` style in `src/scss/3-components/_card.scss`. Because `.card-title` has its own size, the demo pages use `<h2 class="card-title">` under the page's `<h1>`. That keeps the heading order right for screen readers.

## Header with a subtitle and actions

> `CardActions` is only available in **AdminKit PRO**.

Give a card a menu when its content has actions of its own. The dashboards put that menu in the card header with `CardActions`. Render it before the title, since it floats to the right. Its menu items are placeholders: set your own in `src/components/ui/card-actions.tsx`.

```html
<div class="card" style="max-width: 24rem;">
  <div class="card-header">
    <div class="card-actions float-end">
      <div class="dropdown position-relative">
        <a href="#" data-bs-toggle="dropdown" data-bs-display="static" aria-label="Card actions"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-ellipsis align-middle" aria-hidden="true"><circle cx="12" cy="12" r="1"/><circle cx="19" cy="12" r="1"/><circle cx="5" cy="12" r="1"/></svg></a>
        <div class="dropdown-menu dropdown-menu-end">
          <a class="dropdown-item" href="#">Action</a>
          <a class="dropdown-item" href="#">Another action</a>
          <a class="dropdown-item" href="#">Something else here</a>
        </div>
      </div>
    </div>
    <h2 class="card-title">Browser usage</h2>
    <p class="h6 card-subtitle text-body-secondary">Last 30 days</p>
  </div>
  <div class="card-body">Chrome leads with 58% of the sessions.</div>
</div>
```
```tsx code
import { CardActions } from "@/components/ui/card-actions";

<div className="card">
  <div className="card-header">
    <CardActions />
    <h2 className="card-title">Browser usage</h2>
    <p className="h6 card-subtitle text-body-secondary">Last 30 days</p>
  </div>
  <div className="card-body">Chrome leads with 58% of the sessions.</div>
</div>
```

## Image and list

To show a picture with the content, put the image before the header. Load files from `public/` with `asset()` from `@/lib/site`. It adds the base path the app is served from:

```tsx
import { asset } from "@/lib/site";

<div className="card">
  <img
    className="card-img-top"
    src={asset("/img/photos/unsplash-1.jpg")}
    alt="Team meeting"
    width="1200"
    height="800"
  />
  <div className="card-header">
    <h2 className="card-title mb-0">Team</h2>
  </div>
  <ul className="list-group list-group-flush">
    <li className="list-group-item">Design review</li>
    <li className="list-group-item">Sprint planning</li>
  </ul>
</div>
```

The `width` and `height` reserve the image's space before it loads. `.card-img-top` scales it to the width of the card.

## Cards from an array

When you have a set of similar items, such as projects, show them as a grid of cards. The grid is a `map` over your data, with a stable `key`:

```tsx
const projects = [
  { id: 1, name: "Website redesign", due: "June 12" },
  { id: 2, name: "Mobile app", due: "July 3" },
];

<div className="row">
  {projects.map((project) => (
    <div key={project.id} className="col-12 col-md-6">
      <div className="card">
        <div className="card-body">
          <h2 className="card-title">{project.name}</h2>
          <p className="card-text text-body-secondary">Due {project.due}</p>
        </div>
      </div>
    </div>
  ))}
</div>
```

Add `h-100` (or `flex-fill`, as on the dashboards) to the cards of one row to give them the same height.

## Clickable cards

Make the whole card clickable when it leads to a page of its own. A card that is a link, `a.card`, or a card with a `.stretched-link` inside lifts on hover and on keyboard focus. It also shows the focus ring. With reduced motion, it does not move. For a route of your app, use React Router's `Link`:

```tsx
import { Link } from "react-router";

<div className="card">
  <div className="card-body">
    <h2 className="card-title">
      <Link to="/pages-projects" className="stretched-link text-reset">
        Website redesign
      </Link>
    </h2>
    <p className="card-text">12 open tasks</p>
  </div>
</div>
```

## Accessibility

- Give every card a heading (`.card-title`) at the right level of the page outline, so screen reader users can jump between cards.
- For a clickable card, prefer a `.stretched-link` on the title: the link's name is then the title, not the whole text of the card.
- Give images an `alt` that describes them, or `alt=""` when they are decorative.

## Related

- [Next.js Bootstrap cards](/nextjs/docs/components/card/): the same markup in AdminKit for Next.js
- [Bootstrap 5 cards](/bootstrap/docs/components/card/): the cards of the HTML version
- [Tables](/react/docs/components/tables/): a table that fills a card edge to edge
- [Cards demo](https://react-demo.adminkit.io/ui-cards)

---

Canonical HTML page: https://adminkit.io/react/docs/components/card/

Site overview for agents: https://adminkit.io/llms.txt (expanded: https://adminkit.io/llms-full.txt) · Documentation: https://adminkit.io/bootstrap/docs/ · Questions: support@adminkit.io
