Card
A content container for grouping related information. Built with Base UI and Tailwind CSS. Copy-paste ready.
This will take a few seconds to complete.
import { CircleAlertIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Card,
CardDescription,
CardFooter,
CardHeader,
CardPanel,
CardTitle,
} from "@/components/ui/card";
import { Field, FieldLabel } from "@/components/ui/field";
import { Form } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import {
Select,
SelectItem,
SelectPopup,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
const frameworkOptions = [
{ label: "Next.js", value: "next" },
{ label: "Vite", value: "vite" },
{ label: "Remix", value: "remix" },
{ label: "Astro", value: "astro" },
];
export default function Particle() {
return (
<Card className="w-full max-w-xs">
<CardHeader>
<CardTitle>Create project</CardTitle>
<CardDescription>Deploy your new project in one-click.</CardDescription>
</CardHeader>
<CardPanel>
<Form className="flex w-full flex-col gap-4">
<Field>
<FieldLabel>Name</FieldLabel>
<Input placeholder="Name of your project" type="text" />
</Field>
<Field>
<FieldLabel>Framework</FieldLabel>
<Select defaultValue="next" items={frameworkOptions}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectPopup>
{frameworkOptions.map(({ label, value }) => (
<SelectItem key={value} value={value}>
{label}
</SelectItem>
))}
</SelectPopup>
</Select>
</Field>
<Button className="w-full" type="submit">
Deploy
</Button>
</Form>
</CardPanel>
<CardFooter>
<div className="flex gap-1 text-muted-foreground text-xs">
<CircleAlertIcon className="size-3 h-lh shrink-0" />
<p>This will take a few seconds to complete.</p>
</div>
</CardFooter>
</Card>
);
}
Installation
pnpm dlx shadcn@latest add @cnippet/card
Usage
import {
Card,
CardDescription,
CardFooter,
CardHeader,
CardPanel,
CardTitle,
} from "@/components/ui/card"<Card>
<CardHeader>
<CardTitle>Title</CardTitle>
<CardDescription>Description</CardDescription>
</CardHeader>
<CardPanel>Content</CardPanel>
<CardFooter>Footer</CardFooter>
</Card>Examples
With Border
Renders the card with an explicit border for clearer visual containment against various background colors.
The header has a border-b class applied, creating a visual separation between the header and content sections.
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
export function Pattern() {
return (
<Card className="w-full max-w-xs">
<CardHeader className="border-b">
<CardTitle>Header with Border</CardTitle>
<CardDescription>
This is a card with a header that has a bottom border.
</CardDescription>
</CardHeader>
<CardContent>
<p>
The header has a border-b class applied, creating a visual separation
between the header and content sections.
</p>
</CardContent>
</Card>
);
}
With Border Separation
Adds dividing borders between the header, content, and footer sections for a more structured layout.
The footer has a border-t class applied, creating a visual separation between the content and footer sections.
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
export function Pattern() {
return (
<Card className="w-full max-w-xs">
<CardHeader className="border-b">
<CardTitle>Header with Border</CardTitle>
</CardHeader>
<CardContent>
<p>
The footer has a border-t class applied, creating a visual separation
between the content and footer sections.
</p>
</CardContent>
<CardFooter className="border-t">
<Button className="w-full" variant="outline">
Action
</Button>
</CardFooter>
</Card>
);
}
With Link
Makes the entire card a navigable link, useful for post, article, or resource cards in grid layouts.
Go to this step by step guideline process on how to certify for your weekly benefits:
import { ExternalLinkIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
export function Pattern() {
return (
<Card className="w-full max-w-xs gap-2 pt-5">
<CardHeader>
<CardTitle>Need a help in Claim?</CardTitle>
</CardHeader>
<CardContent className="mb-2">
<p>
Go to this step by step guideline process on how to certify for your
weekly benefits:
</p>
</CardContent>
<CardFooter className="py-2">
<Button className="px-0" variant="link">
See our guideline
<ExternalLinkIcon aria-hidden="true" />
</Button>
</CardFooter>
</Card>
);
}
With Dropdown Menu
Adds a Menu trigger in the card header for contextual actions like edit, share, or delete.
Go to this step by step guideline process on how to certify for your weekly benefits:
import {
ExternalLinkIcon,
MoreHorizontalIcon,
SettingsIcon,
UserIcon,
} from "lucide-react";
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/menu";
export function Pattern() {
return (
<Card className="w-full max-w-xs gap-2 pt-5">
<CardHeader className="flex items-center justify-between">
<CardTitle>Need a help in Claim?</CardTitle>
<DropdownMenu>
<DropdownMenuTrigger render={<Button size="icon" variant="ghost" />}>
<MoreHorizontalIcon aria-hidden="true" />
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-48">
<DropdownMenuGroup>
<DropdownMenuLabel>Team Settings</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>
<UserIcon aria-hidden="true" />
<span>Manage members</span>
</DropdownMenuItem>
<DropdownMenuItem>
<SettingsIcon aria-hidden="true" />
<span>Team preferences</span>
</DropdownMenuItem>
</DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem>
<ExternalLinkIcon aria-hidden="true" />
<span>Open dashboard</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</CardHeader>
<CardContent className="mb-2">
<p>
Go to this step by step guideline process on how to certify for your
weekly benefits:
</p>
</CardContent>
<CardFooter>
<Button size="sm">
<Avatar className="size-5">
<AvatarImage alt="@shadcn" src="https://github.com/shadcn.png" />
<AvatarFallback>CH</AvatarFallback>
</Avatar>
<span className="text-xs">@shadcn</span>
</Button>
</CardFooter>
</Card>
);
}
With Image
Includes a top image region above the card header for media-rich content or product cards.

Simplifying your workflow from day one. Manage your tasks, projects, and team in one place.
import { ArrowRightIcon, BellIcon, SparklesIcon } from "lucide-react";
import Image from "next/image";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
export function Pattern() {
return (
<Card className="w-full max-w-xs">
<CardContent className="flex flex-col gap-4">
<div className="relative h-48 w-full overflow-hidden rounded-lg">
<Image
alt="16:9"
className="object-cover"
fill
src="https://res.cloudinary.com/dcxm3ccir/image/upload/v1741613286/h1.jpg"
/>
</div>
<div className="flex items-center justify-between gap-5">
<Badge variant="outline">
<BellIcon aria-hidden="true" />
Trending
</Badge>
<div className="flex items-center gap-1">
<SparklesIcon aria-hidden="true" />
<span className="font-medium text-secondary-foreground text-xs">
Featured
</span>
</div>
</div>
<p className="text-foreground text-sm">
Simplifying your workflow from day one. Manage your tasks, projects,
and team in one place.
</p>
<Button>
Get Started
<ArrowRightIcon aria-hidden="true" />
</Button>
</CardContent>
</Card>
);
}
Image Zoom Hover
The card image scales up on hover via a CSS transform, creating a subtle zoom that draws focus to the visual.

Image Scale Effect
This card features a smooth image scaling effect and background overlay on hover.
import Image from "next/image";
import { Card } from "@/components/ui/card";
export function Pattern() {
return (
<Card className="group/card relative h-96 w-full max-w-xs overflow-hidden border-0 p-0!">
<Image
alt="Background"
className="object-cover transition-transform duration-500 group-hover/card:scale-110"
fill
src="https://res.cloudinary.com/dcxm3ccir/image/upload/v1741613286/h1.jpg"
/>
{/* Background fade effects */}
<div className="absolute inset-x-0 bottom-0 h-1/2 bg-linear-to-t from-black/60 to-transparent transition-opacity duration-500 group-hover/card:from-black/70" />
{/* Content */}
<div className="relative flex h-full flex-col justify-end p-6">
<h3 className="font-bold text-white text-xl">Image Scale Effect</h3>
<p className="mt-2 text-sm text-white/90">
This card features a smooth image scaling effect and background
overlay on hover.
</p>
</div>
</Card>
);
}
Image Shadow Fade
A gradient overlay fades from transparent to the card background over the image, making overlaid title text more readable.

Author Profile
Profile card showcasing the author’s avatar, name, and estimated reading time for each post.
import Image from "next/image";
import Link from "next/link";
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { Card, CardContent } from "@/components/ui/card";
const CustomBadge = () => {
return (
<svg className="size-4" viewBox="0 0 15 16">
<path
className="fill-blue-500"
d="M14.5425 6.8973L13.5 5.8398C13.4273 5.76858 13.3699 5.68331 13.3312 5.58919C13.2925 5.49507 13.2734 5.39405 13.275 5.2923V3.7923C13.274 3.58681 13.2324 3.38353 13.1527 3.19414C13.0729 3.00476 12.9565 2.833 12.8101 2.68874C12.6638 2.54448 12.4904 2.43055 12.2998 2.35351C12.1093 2.27647 11.9055 2.23783 11.7 2.2398H10.2C10.0982 2.24141 9.99722 2.22228 9.9031 2.1836C9.80898 2.14492 9.72371 2.08749 9.65249 2.0148L8.60249 0.957304C8.30998 0.665106 7.91344 0.500977 7.49999 0.500977C7.08654 0.500977 6.68999 0.665106 6.39749 0.957304L5.33999 1.9998C5.26876 2.07249 5.1835 2.12992 5.08937 2.1686C4.99525 2.20728 4.89424 2.22641 4.79249 2.2248H3.29249C3.08699 2.22578 2.88371 2.26735 2.69432 2.34713C2.50494 2.4269 2.33318 2.54331 2.18892 2.68966C2.04466 2.83602 1.93073 3.00943 1.85369 3.19994C1.77665 3.39046 1.73801 3.59431 1.73999 3.7998V5.2998C1.74159 5.40155 1.72247 5.50256 1.68378 5.59669C1.6451 5.69081 1.58767 5.77608 1.51499 5.8473L0.457487 6.8973C0.165289 7.18981 0.00115967 7.58635 0.00115967 7.9998C0.00115967 8.41325 0.165289 8.80979 0.457487 9.1023L1.49999 10.1598C1.57267 10.231 1.6301 10.3163 1.66878 10.4104C1.70747 10.5045 1.72659 10.6056 1.72499 10.7073V12.2073C1.72597 12.4128 1.76754 12.6161 1.84731 12.8055C1.92709 12.9949 2.04349 13.1666 2.18985 13.3109C2.3362 13.4551 2.50961 13.5691 2.70013 13.6461C2.89064 13.7231 3.0945 13.7618 3.29999 13.7598H4.79999C4.90174 13.7582 5.00275 13.7773 5.09687 13.816C5.191 13.8547 5.27627 13.9121 5.34749 13.9848L6.40499 15.0423C6.69749 15.3345 7.09404 15.4986 7.50749 15.4986C7.92094 15.4986 8.31748 15.3345 8.60999 15.0423L9.65999 13.9998C9.73121 13.9271 9.81647 13.8697 9.9106 13.831C10.0047 13.7923 10.1057 13.7732 10.2075 13.7748H11.7075C12.1212 13.7748 12.518 13.6104 12.8106 13.3179C13.1031 13.0253 13.2675 12.6285 13.2675 12.2148V10.7148C13.2659 10.6131 13.285 10.512 13.3237 10.4179C13.3624 10.3238 13.4198 10.2385 13.4925 10.1673L14.55 9.1098C14.6953 8.96434 14.8104 8.79157 14.8887 8.60146C14.9671 8.41134 15.007 8.20761 15.0063 8.00199C15.0056 7.79638 14.9643 7.59293 14.8847 7.40334C14.8051 7.21376 14.6888 7.04178 14.5425 6.8973Z"
/>
<path
className="fill-white"
d="M10.635 6.6498L6.95249 10.2498C6.90055 10.3024 6.83864 10.3441 6.77038 10.3724C6.70212 10.4007 6.62889 10.4152 6.55499 10.4148C6.48062 10.4138 6.40719 10.398 6.33896 10.3684C6.27073 10.3388 6.20905 10.2959 6.15749 10.2423L4.37999 8.4423C4.32532 8.39026 4.28169 8.32775 4.25169 8.25849C4.22169 8.18923 4.20593 8.11464 4.20536 8.03916C4.20479 7.96369 4.21941 7.88887 4.24836 7.81916C4.27731 7.74946 4.31999 7.68629 4.37387 7.63342C4.42774 7.58056 4.4917 7.53908 4.56194 7.51145C4.63218 7.48382 4.70726 7.47061 4.78271 7.4726C4.85816 7.4746 4.93244 7.49176 5.00112 7.52306C5.0698 7.55436 5.13148 7.59917 5.18249 7.6548L6.56249 9.0573L9.84749 5.8473C9.95296 5.74197 10.0959 5.6828 10.245 5.6828C10.394 5.6828 10.537 5.74197 10.6425 5.8473C10.6953 5.90016 10.737 5.963 10.7653 6.03216C10.7935 6.10132 10.8077 6.17542 10.807 6.25013C10.8063 6.32483 10.7908 6.39865 10.7612 6.46728C10.7317 6.5359 10.6888 6.59795 10.635 6.6498Z"
/>
</svg>
);
};
export function Pattern() {
return (
<Card className="w-full max-w-sm p-0">
<CardContent className="flex items-center gap-5 p-0">
<div className="group/card relative flex h-96 w-full flex-col justify-end overflow-hidden">
<Image
alt="16:9"
className="object-cover transition-transform duration-500 group-hover/card:scale-110"
fill
src="https://res.cloudinary.com/dcxm3ccir/image/upload/v1741613286/h1.jpg"
/>
{/* Background fade effects */}
<div className="absolute inset-x-0 top-0 h-1/2 bg-linear-to-b from-black/60 to-transparent" />
<div className="absolute inset-x-0 bottom-0 h-1/2 bg-linear-to-t from-black/60 to-transparent" />
{/* Header */}
<div className="absolute top-0 right-0 left-0 flex flex-wrap items-center gap-3 p-6">
<div className="relative">
<Avatar className="size-10">
<AvatarImage
alt="James Brown"
src="https://images.unsplash.com/photo-1527980965255-d3b416303d12?w=96&h=96&dpr=2&q=80"
/>
<AvatarFallback>JB</AvatarFallback>
</Avatar>
<span className="absolute -top-0.5 -right-0.5">
<CustomBadge />
</span>
</div>
<div className="flex-1 space-y-px">
<Link className="font-medium text-white" href="#">
Nick Johnson
</Link>
<div className="text-white/80">nick@example.com</div>
</div>
<Badge variant="success">New</Badge>
</div>
{/* Content */}
<div className="absolute right-0 bottom-0 left-0 space-y-2 p-6">
<h3 className="font-semibold text-2xl text-white">
Author Profile
</h3>
<p className="text-white">
Profile card showcasing the author’s avatar, name, and estimated
reading time for each post.
</p>
</div>
</div>
</CardContent>
</Card>
);
}
Stacked Depth
Renders three offset card layers with slight shadows to visually suggest a stack of items or a deck of content.
The card component supports a size prop that defaults to "default" for standard spacing and sizing.
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
export function Pattern() {
return (
<div className="relative size-fit">
{/* Card */}
<Card className="relative z-1 w-full max-w-xs">
<CardHeader>
<CardTitle>Default Card</CardTitle>
<CardDescription>
This card uses the default size variant.
</CardDescription>
</CardHeader>
<CardContent>
<p>
The card component supports a size prop that defaults to
"default" for standard spacing and sizing.
</p>
</CardContent>
<CardFooter>
<Button className="w-full" variant="outline">
Action
</Button>
</CardFooter>
</Card>
{/* Depth effect */}
<div className="absolute inset-4 -bottom-3 z-0 rounded-lg border bg-card shadow-black/1 shadow-md" />
<div className="absolute inset-2 -bottom-1.5 z-0 rounded-lg border bg-card shadow-black/1 shadow-md" />
</div>
);
}
Login Form Card
A full login form contained within a card, including email, password fields, and a submit button.
By clicking continue, you agree to our Terms of Service
"use client";
import { EyeIcon, EyeOffIcon, GitBranch } from "lucide-react";
import { useState } from "react";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Field, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import {
InputGroup,
InputGroupAddon,
InputGroupInput,
} from "@/components/ui/input-group";
import { Separator } from "@/components/ui/separator";
export function Pattern() {
const [isVisible, setIsVisible] = useState(false);
return (
<Card className="mx-auto w-full max-w-xs">
<CardHeader>
<CardTitle>Sign in</CardTitle>
<CardDescription>
Enter your email and password to access your account
</CardDescription>
</CardHeader>
<CardContent>
<form className="grid gap-6" onSubmit={(e) => e.preventDefault()}>
<div className="grid gap-4">
<Field>
<FieldLabel htmlFor="email-12">Email address</FieldLabel>
<Input
id="email-12"
placeholder="name@example.com"
required
type="email"
/>
</Field>
<Field>
<div className="flex items-center justify-between">
<FieldLabel htmlFor="password-12">Password</FieldLabel>
<a
className="text-muted-foreground text-xs underline-offset-4 hover:underline"
href="#"
>
Forgot password?
</a>
</div>
<InputGroup>
<InputGroupInput
id="password-12"
placeholder="Password"
required
type={isVisible ? "text" : "password"}
/>
<InputGroupAddon align="inline-end">
<Button
aria-label={isVisible ? "Hide password" : "Show password"}
onClick={() => setIsVisible(!isVisible)}
size="icon-sm"
variant="ghost"
>
{isVisible ? (
<EyeOffIcon aria-hidden="true" />
) : (
<EyeIcon aria-hidden="true" />
)}
</Button>
</InputGroupAddon>
</InputGroup>
</Field>
</div>
<div className="flex flex-col gap-6">
<Button className="w-full" type="submit">
Sign in
</Button>
<div className="flex items-center gap-3 text-muted-foreground text-xs">
<Separator className="flex-1" />
<span>Or continue with</span>
<Separator className="flex-1" />
</div>
<Button className="w-full" variant="outline">
<GitBranch aria-hidden="true" />
GitBranch
</Button>
</div>
</form>
</CardContent>
<CardFooter>
<p className="w-full text-center text-muted-foreground text-xs">
By clicking continue, you agree to our{" "}
<a
className="underline underline-offset-4 hover:text-primary"
href="#"
>
Terms of Service
</a>
</p>
</CardFooter>
</Card>
);
}
Expandable Content
A card that expands in-place to reveal additional content, toggled by a "Show more" button at the bottom.
"use client";
import { ChevronDownIcon } from "lucide-react";
import { useState } from "react";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import {
Card,
CardAction,
CardContent,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Progress } from "@/components/ui/progress";
export function Pattern() {
const [isOpen, setIsOpen] = useState(false);
return (
<Card className="relative w-full max-w-md gap-6 overflow-visible pb-1">
<CardHeader className="flex items-center justify-between">
<CardTitle>3 days remaining in cycle</CardTitle>
<CardAction>
<Button size="sm" variant="outline">
Billing
</Button>
</CardAction>
</CardHeader>
<CardContent
className={cn(
"relative space-y-5 overflow-hidden transition-all duration-500 ease-in-out",
isOpen ? "max-h-125" : "max-h-48",
)}
>
{/* Usage Details */}
<div className="space-y-3 rounded-lg bg-muted/60 p-4">
<div className="flex justify-between font-medium text-muted-foreground text-xs">
<span>Included Credit</span>
<span>On-Demand Charges</span>
</div>
<div className="flex justify-between font-bold text-lg">
<span>$18.08 / $20</span>
<span>$0</span>
</div>
<Progress className="h-2" value={90} />
</div>
{/* Additional Usage Details */}
<div className="flex flex-col gap-4">
<div className="flex justify-between text-sm">
<span className="font-medium text-foreground">Requests</span>
<span className="text-muted-foreground">$210.84</span>
</div>
<div className="flex justify-between text-sm">
<span className="font-medium text-foreground">Active CPU</span>
<span className="text-muted-foreground">$21.95</span>
</div>
<div className="flex justify-between text-sm">
<span className="font-medium text-foreground">Events</span>
<span className="text-muted-foreground">$21.20</span>
</div>
<div className="flex justify-between text-sm">
<span className="font-medium text-foreground">Storage Usage</span>
<span className="text-muted-foreground">$20.45</span>
</div>
<div className="flex justify-between text-sm">
<span className="font-medium text-foreground">Bandwidth</span>
<span className="text-muted-foreground">$0.00</span>
</div>
</div>
{/* Faded background effect for collapsed state */}
<div
className={cn(
"pointer-events-none absolute inset-x-0 bottom-0 h-20 rounded-b-lg bg-linear-to-t from-background to-transparent transition-opacity duration-300",
isOpen ? "opacity-0" : "opacity-100",
)}
/>
</CardContent>
{/* Toggle button */}
<div className="absolute -bottom-4 left-1/2 -translate-x-1/2">
<Button
className="rounded-full bg-background shadow-sm hover:bg-background"
onClick={() => setIsOpen(!isOpen)}
size="icon-sm"
variant="outline"
>
<ChevronDownIcon
aria-hidden="true"
className={cn(
"transition-transform duration-300",
isOpen && "rotate-180",
)}
/>
<span className="sr-only">Toggle card</span>
</Button>
</div>
</Card>
);
}
With Overflow Menu
Places a three-dot overflow icon button in the card header for secondary actions that don't warrant dedicated buttons.
Revenue
import {
ArrowDownIcon,
ArrowUpIcon,
MoreHorizontalIcon,
PinIcon,
SettingsIcon,
Share2Icon,
TrashIcon,
TriangleAlertIcon,
} from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/menu";
import { Separator } from "@/components/ui/separator";
export function Pattern() {
const title = "Revenue";
const value = "$12.4k";
const delta = 12.5;
const positive = true;
const lastMonth = "$11.0k";
return (
<Card className="w-full max-w-xs">
<CardContent className="flex flex-col gap-5">
<div className="flex items-center justify-between gap-3">
<h3 className="font-medium text-muted-foreground text-sm">{title}</h3>
<DropdownMenu>
<DropdownMenuTrigger
render={
<Button
aria-label="More options"
className="-me-1.5"
size="icon"
variant="ghost"
/>
}
>
<MoreHorizontalIcon aria-hidden="true" />
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-48">
<DropdownMenuGroup>
<DropdownMenuItem>
<SettingsIcon aria-hidden="true" />
Settings
</DropdownMenuItem>
<DropdownMenuItem>
<TriangleAlertIcon aria-hidden="true" />
Add Alert
</DropdownMenuItem>
<DropdownMenuItem>
<PinIcon aria-hidden="true" />
Pin to Dashboard
</DropdownMenuItem>
<DropdownMenuItem>
<Share2Icon aria-hidden="true" />
Share
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem variant="destructive">
<TrashIcon aria-hidden="true" />
Remove
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
</div>
<div className="space-y-2.5">
<div className="flex items-center gap-2.5">
<span className="font-medium text-2xl text-foreground tabular-nums tracking-tight">
{value}
</span>
<Badge variant={positive ? "success" : "destructive"}>
{positive ? (
<ArrowUpIcon aria-hidden="true" />
) : (
<ArrowDownIcon aria-hidden="true" />
)}
{delta}%
</Badge>
</div>
<Separator />
<div className="text-muted-foreground text-xs">
Vs last month:{" "}
<span className="font-medium text-foreground tabular-nums">
{lastMonth}
</span>
</div>
</div>
</CardContent>
</Card>
);
}
With Header Actions
Adds a Badge and action buttons in the card header row, a common pattern for dashboard widget titles.
Integration name
InstalledShort description of the integration and what it does in one line.
import { CheckIcon, MoreVerticalIcon } from "lucide-react";
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/components/ui/avatar";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/menu";
export function Pattern() {
return (
<Card className="w-full max-w-sm p-0">
<CardContent className="p-0">
<div className="flex items-center justify-between border-b px-3 py-2">
<Badge variant="secondary">
<CheckIcon aria-hidden="true" />
Live
</Badge>
<DropdownMenu>
<DropdownMenuTrigger
render={
<Button
aria-label="More options"
className="text-muted-foreground"
size="icon"
variant="ghost"
/>
}
>
<MoreVerticalIcon aria-hidden="true" />
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-32">
<DropdownMenuGroup>
<DropdownMenuItem>Edit</DropdownMenuItem>
<DropdownMenuItem>Copy link</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
</div>
<div className="space-y-3 p-4">
<div className="flex items-start justify-between gap-2">
<h3 className="font-medium text-sm leading-tight">
Integration name
</h3>
<Badge size="sm" variant="success">
Installed
</Badge>
</div>
<p className="text-muted-foreground text-sm">
Short description of the integration and what it does in one line.
</p>
<div className="flex -space-x-2">
<Avatar className="size-6 ring-2 ring-background">
<AvatarImage
alt="User 1"
src="https://images.unsplash.com/photo-1519699047748-de8e457a634e?w=96&h=96&dpr=2&q=80"
/>
<AvatarFallback>SC</AvatarFallback>
</Avatar>
<Avatar className="size-6 ring-2 ring-background">
<AvatarImage
alt="User 2"
src="https://images.unsplash.com/photo-1584308972272-9e4e7685e80f?w=96&h=96&dpr=2&q=80"
/>
<AvatarFallback>MR</AvatarFallback>
</Avatar>
<Avatar className="size-6 ring-2 ring-background">
<AvatarImage
alt="User 3"
src="https://images.unsplash.com/photo-1485893086445-ed75865251e0?w=96&h=96&dpr=2&q=80"
/>
<AvatarFallback>EW</AvatarFallback>
</Avatar>
<div className="flex size-6 items-center justify-center rounded-full border bg-muted text-[10px] ring-2 ring-background">
+3
</div>
</div>
</div>
<div className="border-t p-3">
<Button className="w-full" variant="outline">
Open
</Button>
</div>
</CardContent>
</Card>
);
}
With Icon and Link
Combines a leading icon, a title, and a trailing "View" link in the card header for resource or feature entry points.
Track and review all recent purchases, updates, and status changes in one place.
View Ordersimport { ChevronRightIcon, ShoppingBagIcon } from "lucide-react";
import { Card, CardContent } from "@/components/ui/card";
const item = {
description:
"Track and review all recent purchases, updates, and status changes in one place.",
icon: <ShoppingBagIcon aria-hidden="true" />,
link: "View Orders",
title: "Recent Orders Overview",
};
export function Pattern() {
return (
<Card className="w-full max-w-xs">
<CardContent className="flex flex-col gap-3">
<div className="flex size-11 items-center justify-center rounded-md bg-primary [&_svg]:size-5 [&_svg]:text-primary-foreground">
{item.icon}
</div>
<a
className="block font-medium text-foreground text-sm leading-tight hover:text-primary"
href="#"
>
{item.title}
</a>
<p className="text-muted-foreground text-xs leading-relaxed">
{item.description}
</p>
<a
className="inline-flex items-center gap-1 font-medium text-primary text-xs hover:underline"
href="#"
>
{item.link}
<ChevronRightIcon aria-hidden="true" className="size-2.5 shrink-0" />
</a>
</CardContent>
</Card>
);
}
With Header Label and Link
Pairs a category label with a navigation link in the header, useful for classification-style cards that need external navigation.
Find guides, API references, and examples to integrate with our platform.
View docsimport { BookOpenIcon, LinkIcon } from "lucide-react";
import { Card, CardContent } from "@/components/ui/card";
const item = {
description:
"Find guides, API references, and examples to integrate with our platform.",
icon: <BookOpenIcon aria-hidden="true" />,
label: "Documentation",
link: "View docs",
};
export function Pattern() {
return (
<Card className="w-full max-w-xs p-0">
<CardContent className="p-0">
<div className="border-b px-4 py-3">
<div className="flex items-center gap-2 text-muted-foreground [&_svg]:size-4">
{item.icon}
<span className="font-medium text-foreground text-sm">
{item.label}
</span>
</div>
</div>
<div className="space-y-3 p-4">
<p className="text-muted-foreground text-sm leading-relaxed">
{item.description}
</p>
<a
className="inline-flex items-center gap-1 font-medium text-primary text-xs hover:underline"
href="#"
>
<LinkIcon aria-hidden="true" className="size-2.5 shrink-0" />
{item.link}
</a>
</div>
</CardContent>
</Card>
);
}
Repository Cards
A list of GitHub-style repository cards each showing the name, description, language badge with color dot, star count, and fork count.
A UI component library built with Base UI and Tailwind CSS.
Minimal blogging starter with MDX, Tailwind, and Next.js.
Lightweight state manager for React with zero boilerplate.
import { GitBranch, GitForkIcon, StarIcon } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Card, CardContent } from "@/components/ui/card";
const repos = [
{
description: "A UI component library built with Base UI and Tailwind CSS.",
forks: 248,
langColor: "bg-blue-500",
language: "TypeScript",
name: "ui-cnippet",
stars: 1_420,
},
{
description: "Minimal blogging starter with MDX, Tailwind, and Next.js.",
forks: 91,
langColor: "bg-blue-500",
language: "TypeScript",
name: "next-blog-starter",
stars: 673,
},
{
description: "Lightweight state manager for React with zero boilerplate.",
forks: 57,
langColor: "bg-yellow-400",
language: "JavaScript",
name: "micro-store",
stars: 312,
},
];
function fmt(n: number) {
return n >= 1000 ? `${(n / 1000).toFixed(1)}k` : String(n);
}
export function Pattern() {
return (
<div className="flex w-full max-w-sm flex-col gap-3">
{repos.map((repo) => (
<Card className="w-full" key={repo.name}>
<CardContent className="flex flex-col gap-2.5">
<div className="flex items-start justify-between gap-2">
<div className="flex items-center gap-2">
<GitBranch className="size-4 shrink-0 text-muted-foreground" />
<span className="font-semibold text-sm">{repo.name}</span>
</div>
<Badge size="sm" variant="secondary">
Public
</Badge>
</div>
<p className="text-muted-foreground text-xs leading-relaxed">
{repo.description}
</p>
<div className="flex items-center gap-4 text-muted-foreground text-xs">
<span className="flex items-center gap-1">
<span className={`size-2.5 rounded-full ${repo.langColor}`} />
{repo.language}
</span>
<span className="flex items-center gap-1">
<StarIcon className="size-3" />
{fmt(repo.stars)}
</span>
<span className="flex items-center gap-1">
<GitForkIcon className="size-3" />
{fmt(repo.forks)}
</span>
</div>
</CardContent>
</Card>
))}
</div>
);
}
Settings Card
A card split into a vertical sidebar nav and a content panel — clicking a nav item swaps the description panel without leaving the card.
Profile Settings
Update your display name, avatar, and contact information.
"use client";
import { BellIcon, CreditCardIcon, ShieldIcon, UserIcon } from "lucide-react";
import { useState } from "react";
import { Card, CardContent } from "@/components/ui/card";
const sections = [
{ icon: UserIcon, id: "profile", label: "Profile" },
{ icon: BellIcon, id: "notifications", label: "Notifications" },
{ icon: CreditCardIcon, id: "billing", label: "Billing" },
{ icon: ShieldIcon, id: "security", label: "Security" },
];
const content: Record<string, { description: string; title: string }> = {
billing: {
description: "Manage your subscription, invoices, and payment methods.",
title: "Billing & Plans",
},
notifications: {
description: "Control how and when you receive alerts and digest emails.",
title: "Notification Preferences",
},
profile: {
description: "Update your display name, avatar, and contact information.",
title: "Profile Settings",
},
security: {
description: "Set a strong password and enable two-factor authentication.",
title: "Security Settings",
},
};
export function Pattern() {
const [active, setActive] = useState("profile");
const panel = content[active];
return (
<Card className="w-full max-w-sm p-0">
<CardContent className="flex gap-0 p-0">
<nav className="flex w-36 shrink-0 flex-col gap-0.5 border-r p-2">
{sections.map(({ icon: Icon, id, label }) => (
<button
className={`flex items-center gap-2 rounded-md px-3 py-2 text-left text-sm transition-colors ${
active === id
? "bg-primary text-primary-foreground"
: "text-muted-foreground hover:bg-muted"
}`}
key={id}
onClick={() => setActive(id)}
type="button"
>
<Icon className="size-3.5 shrink-0" />
{label}
</button>
))}
</nav>
<div className="flex flex-col gap-2 p-4">
<h3 className="font-semibold text-sm">{panel?.title}</h3>
<p className="text-muted-foreground text-xs leading-relaxed">
{panel?.description}
</p>
</div>
</CardContent>
</Card>
);
}
Pricing Plan
A highlighted pricing plan card with a "Most Popular" badge, monthly price, feature checklist with check icons, and a primary CTA button.
- Unlimited components
- Team collaboration
- Priority support
- Custom themes
- CLI access
import { CheckIcon } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Separator } from "@/components/ui/separator";
const plan = {
badge: "Most Popular",
cta: "Start free trial",
features: [
"Unlimited components",
"Team collaboration",
"Priority support",
"Custom themes",
"CLI access",
],
name: "Pro",
period: "/ month",
price: "$12",
};
export function Pattern() {
return (
<Card className="w-full max-w-xs ring-2 ring-primary">
<CardContent className="flex flex-col gap-4">
<div className="flex items-center justify-between">
<span className="font-semibold text-sm">{plan.name}</span>
<Badge size="sm">{plan.badge}</Badge>
</div>
<div className="flex items-baseline gap-1">
<span className="font-bold text-3xl">{plan.price}</span>
<span className="text-muted-foreground text-sm">{plan.period}</span>
</div>
<Separator />
<ul className="flex flex-col gap-2">
{plan.features.map((f) => (
<li className="flex items-center gap-2 text-sm" key={f}>
<CheckIcon className="size-4 shrink-0 text-emerald-500" />
{f}
</li>
))}
</ul>
<Button className="w-full">{plan.cta}</Button>
</CardContent>
</Card>
);
}
Event Card
An event detail card with date, time, location, and attendee count displayed in a meta list, plus a "Reserve a Spot" button and remaining-spots warning.
Design Systems in Practice
Workshopimport { CalendarIcon, ClockIcon, MapPinIcon, UsersIcon } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
const event = {
attendees: 42,
category: "Workshop",
date: "Jun 18, 2026",
location: "Design Hub, Floor 3",
spots: 10,
time: "2:00 PM – 4:30 PM",
title: "Design Systems in Practice",
};
export function Pattern() {
return (
<Card className="w-full max-w-xs">
<CardContent className="flex flex-col gap-4">
<div className="flex items-start justify-between gap-2">
<h3 className="font-semibold text-sm leading-snug">{event.title}</h3>
<Badge size="sm" variant="secondary">
{event.category}
</Badge>
</div>
<div className="flex flex-col gap-2 text-muted-foreground text-xs">
<span className="flex items-center gap-2">
<CalendarIcon className="size-3.5 shrink-0" />
{event.date}
</span>
<span className="flex items-center gap-2">
<ClockIcon className="size-3.5 shrink-0" />
{event.time}
</span>
<span className="flex items-center gap-2">
<MapPinIcon className="size-3.5 shrink-0" />
{event.location}
</span>
<span className="flex items-center gap-2">
<UsersIcon className="size-3.5 shrink-0" />
{event.attendees} attending ·{" "}
<span className="text-amber-600 dark:text-amber-400">
{event.spots} spots left
</span>
</span>
</div>
<Button className="w-full" size="sm">
Reserve a Spot
</Button>
</CardContent>
</Card>
);
}
Stats Dashboard
A 2×2 grid of metric tiles each showing a label, large value, and a trend indicator with up/down icon and percentage delta versus last month.
import { TrendingDownIcon, TrendingUpIcon } from "lucide-react";
import { Card, CardContent } from "@/components/ui/card";
const stats = [
{ delta: "+14.2%", label: "Total Users", trend: "up", value: "84,210" },
{ delta: "+7.8%", label: "Monthly Revenue", trend: "up", value: "$31,540" },
{ delta: "-3.1%", label: "Churn Rate", trend: "down", value: "2.4%" },
{ delta: "+22.5%", label: "Active Sessions", trend: "up", value: "12,840" },
];
export function Pattern() {
return (
<div className="grid w-full max-w-lg grid-cols-2 gap-3">
{stats.map((stat) => (
<Card className="w-full" key={stat.label}>
<CardContent className="flex flex-col gap-1.5">
<span className="text-muted-foreground text-xs">{stat.label}</span>
<span className="font-bold text-xl tabular-nums">{stat.value}</span>
<span
className={`flex items-center gap-1 font-medium text-xs ${
stat.trend === "up"
? "text-emerald-600 dark:text-emerald-400"
: "text-red-500 dark:text-red-400"
}`}
>
{stat.trend === "up" ? (
<TrendingUpIcon className="size-3.5" />
) : (
<TrendingDownIcon className="size-3.5" />
)}
{stat.delta} vs last month
</span>
</CardContent>
</Card>
))}
</div>
);
}
On This Page

