Shadcn Card
Free shadcn/ui card component for React — a styled container with header, title, description, content, footer, and a top-right CardAction slot. Login, stats, pricing, image, and action-menu cards. The examples official shadcn never spelled out.
Installation
{
"registries": {
"@designrevision": "https://registry.designrevision.com/r/{name}.json"
}
}
Add to your existing components.json. Requires shadcn/ui v2.3+.
Packages
Props
No props documented yet.
import * as React from "react"
import { cn } from "@/lib/utils"
function Card({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card"
className={cn(
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
className
)}
{...props}
/>
)
}
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-header"
className={cn(
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
className
)}
{...props}
/>
)
}
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-title"
className={cn("leading-none font-semibold", className)}
{...props}
/>
)
}
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-description"
className={cn("text-muted-foreground text-sm", className)}
{...props}
/>
)
}
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-action"
className={cn(
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
className
)}
{...props}
/>
)
}
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-content"
className={cn("px-6", className)}
{...props}
/>
)
}
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-footer"
className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
{...props}
/>
)
}
export {
Card,
CardHeader,
CardFooter,
CardTitle,
CardAction,
CardDescription,
CardContent,
}
Examples
Card
Every part in one place — a header with a `CardAction`, a content form, and a footer.
Packages
Props
No props documented yet.
import { Button } from "@/components/ui/button"
import {
Card,
CardAction,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
export default function CardDemo01() {
return (
<Card className="w-[360px]">
<CardHeader>
<CardTitle>Create project</CardTitle>
<CardDescription>Deploy your new project in one click.</CardDescription>
<CardAction>
<Button variant="outline" size="sm">
Import
</Button>
</CardAction>
</CardHeader>
<CardContent className="grid gap-4">
<div className="grid gap-2">
<Label htmlFor="project">Name</Label>
<Input id="project" placeholder="Name of your project" />
</div>
<div className="grid gap-2">
<Label htmlFor="framework">Framework</Label>
<Input id="framework" placeholder="Next.js" />
</div>
</CardContent>
<CardFooter className="justify-between">
<Button variant="ghost">Cancel</Button>
<Button>Deploy</Button>
</CardFooter>
</Card>
)
}
Login form
A sign-in card with email/password fields, a forgot-password link, and primary + OAuth [buttons](/components/button).
Packages
Props
No props documented yet.
import { Button } from "@/components/ui/button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
export default function CardLogin01() {
return (
<Card className="w-[360px]">
<CardHeader>
<CardTitle>Login to your account</CardTitle>
<CardDescription>Enter your email below to sign in.</CardDescription>
</CardHeader>
<CardContent className="grid gap-4">
<div className="grid gap-2">
<Label htmlFor="email">Email</Label>
<Input id="email" type="email" placeholder="[email protected]" />
</div>
<div className="grid gap-2">
<div className="flex items-center justify-between">
<Label htmlFor="password">Password</Label>
<a
href="#"
className="text-sm underline-offset-4 hover:underline"
>
Forgot?
</a>
</div>
<Input id="password" type="password" />
</div>
</CardContent>
<CardFooter className="flex-col gap-2">
<Button className="w-full">Sign in</Button>
<Button variant="outline" className="w-full">
Continue with Google
</Button>
</CardFooter>
</Card>
)
}
Stat cards
A row of metric cards — a label, a big number, and a trend [Badge](/components/badge) in the `CardAction` slot.
Packages
Props
No props documented yet.
import { TrendingDownIcon, TrendingUpIcon } from "lucide-react"
import { Badge } from "@/components/ui/badge"
import {
Card,
CardAction,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card"
const stats = [
{ label: "Total Revenue", value: "$45,231.89", change: "+20.1%", up: true },
{ label: "Subscriptions", value: "+2,350", change: "+180.1%", up: true },
{ label: "Churn rate", value: "1.2%", change: "-0.4%", up: false },
]
export default function CardStats01() {
return (
<div className="grid gap-4 sm:grid-cols-3">
{stats.map((stat) => (
<Card key={stat.label} className="gap-2">
<CardHeader>
<CardDescription>{stat.label}</CardDescription>
<CardTitle className="text-2xl tabular-nums">{stat.value}</CardTitle>
<CardAction>
<Badge variant="secondary" className="gap-1">
{stat.up ? (
<TrendingUpIcon className="size-3" />
) : (
<TrendingDownIcon className="size-3" />
)}
{stat.change}
</Badge>
</CardAction>
</CardHeader>
</Card>
))}
</div>
)
}
Settings card
A notifications card with labelled [Switch](/components/switch) rows — the toggleable-settings panel.
Packages
Props
No props documented yet.
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import { Label } from "@/components/ui/label"
import { Switch } from "@/components/ui/switch"
const settings = [
{ id: "comments", label: "Comments", desc: "When someone comments on your post.", checked: true },
{ id: "mentions", label: "Mentions", desc: "When someone @mentions you.", checked: true },
{ id: "follows", label: "Follows", desc: "When you gain a new follower.", checked: false },
]
export default function CardNotifications01() {
return (
<Card className="w-[400px]">
<CardHeader>
<CardTitle>Notifications</CardTitle>
<CardDescription>
Choose what you want to be notified about.
</CardDescription>
</CardHeader>
<CardContent className="grid gap-5">
{settings.map((setting) => (
<div
key={setting.id}
className="flex items-center justify-between gap-4"
>
<div className="space-y-0.5">
<Label htmlFor={setting.id}>{setting.label}</Label>
<p className="text-muted-foreground text-xs">{setting.desc}</p>
</div>
<Switch id={setting.id} defaultChecked={setting.checked} />
</div>
))}
</CardContent>
</Card>
)
}
Pricing card
A pricing tier — a "Most popular" [Badge](/components/badge), a price, a check-listed feature set, and a CTA.
Packages
Props
No props documented yet.
import { CheckIcon } from "lucide-react"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import {
Card,
CardAction,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card"
const features = [
"Unlimited projects",
"Advanced analytics",
"24/7 priority support",
"Custom domains",
"Team collaboration",
]
export default function CardPricing01() {
return (
<Card className="w-[340px]">
<CardHeader>
<CardAction>
<Badge>Most popular</Badge>
</CardAction>
<CardTitle>Pro</CardTitle>
<CardDescription>For growing teams that need more.</CardDescription>
<div className="pt-2">
<span className="text-3xl font-bold">$29</span>
<span className="text-muted-foreground"> / month</span>
</div>
</CardHeader>
<CardContent>
<ul className="grid gap-2 text-sm">
{features.map((feature) => (
<li key={feature} className="flex items-center gap-2">
<CheckIcon className="text-primary size-4 shrink-0" />
{feature}
</li>
))}
</ul>
</CardContent>
<CardFooter>
<Button className="w-full">Get started</Button>
</CardFooter>
</Card>
)
}
Card with image
A media card whose image bleeds to the top edge via `pt-0` + `overflow-hidden`, with a price/CTA footer.
Packages
Props
No props documented yet.
import { Button } from "@/components/ui/button"
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card"
export default function CardImage01() {
return (
<Card className="w-[340px] gap-0 overflow-hidden pt-0">
{/* Swap this for an <img> or next/image. */}
<div className="aspect-video bg-gradient-to-br from-indigo-500 via-purple-500 to-pink-500" />
<CardHeader className="pt-6">
<CardTitle>Mountain Retreat</CardTitle>
<CardDescription>A quiet cabin in the Dolomites.</CardDescription>
</CardHeader>
<CardContent className="text-muted-foreground pt-2 text-sm">
Two nights, breakfast included. Free cancellation up to 48 hours before
check-in.
</CardContent>
<CardFooter className="justify-between pt-6">
<span className="font-semibold">
$240{" "}
<span className="text-muted-foreground text-sm font-normal">
/ night
</span>
</span>
<Button>Book now</Button>
</CardFooter>
</Card>
)
}
Action menu
A `CardAction` holding a "⋮" [Dropdown Menu](/components/dropdown-menu) in the top-right of the header.
Packages
Props
No props documented yet.
import {
MoreVerticalIcon,
PencilIcon,
ShareIcon,
Trash2Icon,
} from "lucide-react"
import { Button } from "@/components/ui/button"
import {
Card,
CardAction,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
export default function CardAction01() {
return (
<Card className="w-[360px]">
<CardHeader>
<CardTitle>Design System</CardTitle>
<CardDescription>Last edited 2 hours ago by Ada.</CardDescription>
<CardAction>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
className="size-8"
aria-label="Open menu"
>
<MoreVerticalIcon />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem>
<PencilIcon />
Edit
</DropdownMenuItem>
<DropdownMenuItem>
<ShareIcon />
Share
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem variant="destructive">
<Trash2Icon />
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</CardAction>
</CardHeader>
<CardContent className="text-muted-foreground text-sm">
A shared library of tokens, components, and guidelines for the product
team.
</CardContent>
</Card>
)
}
Recent activity
A data card listing recent sales — an initials avatar, name/email, and a right-aligned amount.
Packages
Props
No props documented yet.
import { Avatar, AvatarFallback } from "@/components/ui/avatar"
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card"
const sales = [
{ name: "Olivia Martin", email: "[email protected]", amount: "+$1,999.00", initials: "OM" },
{ name: "Jackson Lee", email: "[email protected]", amount: "+$39.00", initials: "JL" },
{ name: "Isabella Nguyen", email: "[email protected]", amount: "+$299.00", initials: "IN" },
{ name: "William Kim", email: "[email protected]", amount: "+$99.00", initials: "WK" },
]
export default function CardData01() {
return (
<Card className="w-[400px]">
<CardHeader>
<CardTitle>Recent sales</CardTitle>
<CardDescription>You made 265 sales this month.</CardDescription>
</CardHeader>
<CardContent className="grid gap-5">
{sales.map((sale) => (
<div key={sale.email} className="flex items-center gap-3">
<Avatar className="size-9">
<AvatarFallback className="text-xs">{sale.initials}</AvatarFallback>
</Avatar>
<div className="grid gap-0.5">
<p className="text-sm font-medium leading-none">{sale.name}</p>
<p className="text-muted-foreground text-xs">{sale.email}</p>
</div>
<span className="ml-auto text-sm font-medium tabular-nums">
{sale.amount}
</span>
</div>
))}
</CardContent>
</Card>
)
}
The Shadcn Card is the container everything else lives in — a styled surface with a header, content, and footer, plus a top-right CardAction slot. It's the canonical shadcn/ui card, with no dependency, and the component you'll reuse most: dashboards, pricing, auth, and settings all sit inside one.
The parts
Seven plain, styled divs:
Card— the surface (rounded-xl border bg-card shadow-sm,py-6,gap-6).CardHeader— a grid holding the title, description, and an optional action.CardTitle/CardDescription— the heading and subtext.CardAction— a top-right slot in the header (see below).CardContent— the body (px-6); put anything here.CardFooter— actions or meta (px-6, flex).
CardAction — the top-right slot
CardHeader is a grid that automatically makes room for a CardAction (has-data-[slot=card-action]:grid-cols-[1fr_auto]), so the title/description flow left while a button, Dropdown Menu, or Badge stays pinned top-right. The Card, Stat cards, Pricing, and Action-menu examples all use it.
It's a container
A Card has no opinion about its content — it's a surface you compose inside. That's why the examples below are really a tour of the rest of the library:
- Forms — a login card with Input + Label + Button (the Login form example).
- Stats — a
CardDescriptionlabel over a bigCardTitlenumber with a trend Badge (the Stat cards example). - Settings — labelled Switch rows (the Settings card example).
- Data — a list of rows with avatars and amounts (the Recent activity example).
Pricing & media
A pricing card is a Card with a "Most popular" badge in CardAction, the price under the description, a check-listed feature set, and a CTA footer (the Pricing card example). For a media card, add pt-0 + overflow-hidden to the Card and drop an image as the first child so it bleeds to the rounded top edge (the Card with image example).
Footers & dividers
CardFooter is flex items-center — use justify-between for a split, or flex-col gap-2 for stacked full-width buttons. To add a divider, put border-b on the header or border-t on the footer; the primitive's [.border-b]:pb-6 / [.border-t]:pt-6 adjust the padding for you.
Theming
The Card reads your design tokens — --card / --card-foreground (the surface), --border, --muted-foreground (descriptions) — so it follows your theme, including dark mode, with no overrides. Restyle a single card with a className: border-primary bg-transparent shadow-none for an outline look, or drop the border for a flat surface.