Skip to content
Caseroom

Components · Layout

Card

A bordered panel with a title, description and footer.

74% of the 2,062 sites in The UI Index have a card (1,534 sites).

Invite your team

Teammates can edit every project in this workspace.

Seats are billed per month. You can remove a member at any time.

Both previews render the same markup; the HTML tab below is that markup without a framework.

Install

React
npx shadcn@latest add https://www.themes.dev/caseroom/r/card.json
Astro
npx shadcn@latest add https://www.themes.dev/caseroom/r/astro/card.json

Or copy the files below into your project. They need clsx, tailwind-merge, and the Caseroom theme. The HTML needs only the compiled CSS.

card.tsx
import type { HTMLAttributes, ReactNode } from 'react';
import { cn } from '@/lib/utils';
import { card } from './card.variants';

export type CardProps = Omit<HTMLAttributes<HTMLDivElement>, 'title'> & {
  title?: ReactNode;
  description?: ReactNode;
  footer?: ReactNode;
  /** The heading level of the title (default h3), so cards fit the page outline. */
  as?: 'h2' | 'h3' | 'h4';
};

/** A bordered panel with an optional title, description and footer. The same props in Astro and React. */
export function Card({ title, description, footer, as: Heading = 'h3', className, children, ...props }: CardProps) {
  return (
    <div className={cn(card.root, className)} {...props}>
      {(title || description) && (
        <div className={card.header}>
          {title && <Heading className={card.title}>{title}</Heading>}
          {description && <p className={card.description}>{description}</p>}
        </div>
      )}
      {children && <div className={card.content}>{children}</div>}
      {footer && <div className={card.footer}>{footer}</div>}
    </div>
  );
}
card.variants.ts
export const card = {
  root: 'flex flex-col gap-5 rounded-xl border bg-card py-5 text-card-foreground',
  header: 'grid gap-1.5 px-5',
  title: 'text-base leading-tight font-semibold tracking-[-0.01em]',
  description: 'text-sm text-muted-foreground',
  content: 'px-5 text-sm',
  footer: 'flex items-center gap-2 border-t px-5 pt-4',
};