Next.js Bootstrap Cards
Next.js Bootstrap card markup for server components: header and title, images with asset(), cards mapped from data, next/link cards and CardActions.
The dashboards of AdminKit for Next.js are built from cards. Each card frames one chart, table or form under a title, so a busy page stays easy to scan. A card is Bootstrap's .card markup in JSX, with no wrapper component. Cards are part of the free and PRO versions.
That plain markup matters in the App Router, where pages are server components that render on the server. A card renders there and needs no "use client". Only what goes inside may be a client component, such as a chart or a dropdown.
Example
As on the demo pages, the title sits in the .card-header and the content in the .card-body:
<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>The style prop takes an object. On a page, size cards with the grid instead, such as <div className="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; a 1px border under the header |
card-title | AdminKit: large, bold and muted at any heading level |
card-subtitle | A normal-weight line under the title; the demos write h6 card-subtitle text-body-secondary |
card-text, card-link | Body text and links |
card-img-top, card-img-bottom | An image on the top or bottom edge, as wide as the card |
list-group list-group-flush | An edge-to-edge list inside the card |
AdminKit styles cards in src/scss/3-components/_card.scss. It adds a shadow, a bottom margin and its own .card-title. Since .card-title sets its own size, the demo pages use <h2 className="card-title"> below the page <h1>. That keeps a correct heading order.
Header with a subtitle and actions
CardActions is only available in AdminKit PRO.
Add a menu to the header when a card has actions of its own. CardActions is the menu in the dashboard card headers. Put it before the title, since it floats right. Its menu items are placeholders: set your own in src/components/ui/card-actions.tsx. It wraps a Dropdown, which is a client component, but it takes no props, so a server component page renders it like any other component:
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>Cards from data in a page
A page that lists items, such as projects, can map its data straight to cards. This runs on the server at build time, so the cards are in the static HTML. For images, asset() from @/lib/site adds the base path to files in public/:
import { asset, pageMetadata } from "@/lib/site";
export const metadata = pageMetadata("/projects");
const projects = [
{ id: 1, name: "Website redesign", due: "June 12", image: 1 },
{ id: 2, name: "Mobile app", due: "July 3", image: 2 },
];
export default function Page() {
return (
<div className="row">
{projects.map((project) => (
<div key={project.id} className="col-12 col-md-6">
<div className="card h-100">
<img
className="card-img-top"
src={asset(`/img/photos/unsplash-${project.image}.jpg`)}
alt={project.name}
width="1200"
height="800"
/>
<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>
);
}pageMetadata() reads the page's title and description from src/data/pages.json, so a new page needs an entry there (see Add a page). The demos use a plain <img> with width and height, which reserve the image's space before it loads. A static export has no image server, so next.config.ts sets images.unoptimized.
Clickable cards
When a card leads somewhere, let the user click anywhere on it. A link card, a.card, or a card with a .stretched-link lifts on hover and keyboard focus and shows the focus ring. With reduced motion, it stays in place. Use next/link for routes of the app:
import Link from "next/link";
<div className="card">
<div className="card-body">
<h2 className="card-title">
<Link href="/pages-projects" className="stretched-link text-reset">
Website redesign
</Link>
</h2>
<p className="card-text">12 open tasks</p>
</div>
</div>Accessibility
- Give each card a heading at the right level, so screen reader users can move from card to card.
- On a clickable card, put the
.stretched-linkon the title: the link is then named after the title, not after every text in the card. - Describe each image in
alt, or usealt=""for decoration.
Related
- React Bootstrap cards: the same markup in AdminKit for React (Vite)
- Bootstrap 5 cards: the cards of the HTML version
- Tables: a table that fills a card edge to edge
- Cards demo
Last updated on
Button group
A Next.js Bootstrap button group for the App Router: groups and toolbars in server components, and a radio group with state in a client component.
Close button
A Next.js Bootstrap close button: Bootstrap's .btn-close with an aria-label, for the modals, offcanvas sheets and alerts of your App Router pages.