React Card Component Examples
Drop-in React Card examples — showcase grid, hover toggle, icon previews, link wrappers, action rows. Zero runtime UI deps. Plain React under Tailwind.
Cards are how interfaces tell users "this is one thing, click it." A component gallery, a plugin marketplace, a product grid, a settings dashboard — each one leans on a tile that pairs a visual region with a short label and signals interactivity on hover. The Card component in Drivn is tuned for exactly that shape: a fixed square footprint, a vertical flex split between preview and info, and a hover lift built into the base style.
This page collects the patterns that come up in shipped products. Each snippet is copy-paste ready and stays consistent with the Card source that the Drivn CLI writes into your repo. The imports use the same @/components/ui/card path the CLI installs under, so the code compiles the moment you run npx drivn add card and paste a snippet into a page.
If the layout you need is taller than it is wide, or if it needs a multi-row body with a footer action bar, override the className to relax the w-48 aspect-square defaults. The slots are plain divs underneath, so any flex or grid arrangement works inside them. Pick the example closest to your case and adapt one prop or one className to fit.
Compound layout with Preview and Info
The default Card composes two slots: Card.Preview fills the top region with flex-1 and centers its child via flex items-center justify-center, and Card.Info sits below with p-5 padding and a horizontal flex split. Drop an icon, image, or illustration into the preview slot, and a title plus description into the info slot.
Because the Card source lives in your repo after install, swapping the rounded corner radius, the border color, or the info padding is a local edit rather than a theme override. The snippet below is the minimum viable Card with both slots: a single emoji as the preview and a two-line label as the info. No wrapper div, no extra state.
1 import { Card } from "@/components/ui/card" 2 3 export default function Page() { 4 return ( 5 <Card> 6 <Card.Preview> 7 <span className="text-2xl">★</span> 8 </Card.Preview> 9 <Card.Info> 10 <div> 11 <p className="text-sm font-semibold text-foreground"> 12 Card title 13 </p> 14 <p className="text-xs text-muted-foreground"> 15 Description text 16 </p> 17 </div> 18 </Card.Info> 19 </Card> 20 ) 21 }
Showcase grid
The Card's w-48 aspect-square default is built for a uniform grid. Pair it with a grid grid-cols-3 gap-4 (or whatever column count fits your viewport) and you get a component gallery, plugin marketplace, or product picker without per-card sizing utilities. Every tile renders 192px wide and square, the hover lift fires consistently, and the gap controls the breathing room between tiles.
For responsive layouts, swap the fixed grid-cols-3 for Tailwind's responsive grid: grid-cols-2 md:grid-cols-3 lg:grid-cols-4. The Card itself does not need to change — it carries its own dimensions, so the parent grid only decides how many columns to render at each breakpoint. This is the pattern Drivn uses for the components index and on the home page.
1 import { Card } from "@/components/ui/card" 2 3 const items = [ 4 { id: 1, icon: "★", name: "Brush" }, 5 { id: 2, icon: "▲", name: "Ruler" }, 6 { id: 3, icon: "✨", name: "Sparkle" }, 7 ] 8 9 export default function Gallery() { 10 return ( 11 <div className="grid grid-cols-3 gap-4"> 12 {items.map((item) => ( 13 <Card key={item.id}> 14 <Card.Preview> 15 <span className="text-2xl">{item.icon}</span> 16 </Card.Preview> 17 <Card.Info> 18 <p className="text-sm font-semibold"> 19 {item.name} 20 </p> 21 </Card.Info> 22 </Card> 23 ))} 24 </div> 25 ) 26 }
Toggling the hover lift
The hover prop defaults to true. When set, the base classes apply hover:bg-accent hover:border-border hover:-translate-y-1 — a 4px upward lift plus a background and border swap. To opt out for static cards (read-only summaries, non-interactive panels), pass hover={false} and the same Card renders without any hover transition.
This matters when a card is wrapped in a non-interactive context — say a dashboard widget that displays a metric without being clickable. A hover lift on a non-interactive element implies affordance the user cannot act on, which is a small but real accessibility annoyance. Setting hover={false} is the right escape hatch. For cards that should look interactive but route to a different action than a click, see the link-wrapper example below.
1 <Card hover={false}> 2 <Card.Preview> 3 <span className="text-2xl">▦</span> 4 </Card.Preview> 5 <Card.Info> 6 <p className="text-sm font-semibold">Static metric</p> 7 </Card.Info> 8 </Card>
Card as a Next.js Link
To make a Card navigate when clicked, wrap it in a Next.js Link. The hover lift already in the Card signals interactivity, and the Link inherits the click target from the entire tile. Pass className="block" (or inline-block) to the Link if its default inline display crops the lift animation.
Keep accessibility in mind: a Link wrapping a Card should have meaningful inner text — a heading inside Card.Info does the job, since screen readers read the heading as the link label. Avoid wrapping a Link around a Card that already contains a Button, because nested interactive elements break keyboard tab order. If your card has both a primary destination and a secondary button action, use a card-level Link plus an onClick handler that calls event.preventDefault() on the inner button.
1 import Link from "next/link" 2 import { Card } from "@/components/ui/card" 3 4 export default function ComponentTile() { 5 return ( 6 <Link href="/docs/components/button" className="block"> 7 <Card> 8 <Card.Preview> 9 <span className="text-2xl">●</span> 10 </Card.Preview> 11 <Card.Info> 12 <p className="text-sm font-semibold">Button</p> 13 </Card.Info> 14 </Card> 15 </Link> 16 ) 17 }
Install Drivn in one command
Copy the source into your project and own every line. Zero runtime dependencies, pure React + Tailwind.
npx drivn@latest createRequires Node 18+. Works with npm, pnpm, and yarn.
Frequently asked questions
No. The Card source imports React from react and a cn class merger from your utils — that is the full runtime. There is no Radix slot, no cva, no clsx wrapper around the children. The component renders to three plain divs (root, preview, info) styled with Tailwind, and the dot notation API is built with Object.assign instead of a context provider.
Pass a className that overrides the width and aspect ratio: <Card className="w-full aspect-auto">…</Card>. The cn utility merges the override with the base classes, so w-full wins over w-48 and aspect-auto releases the square ratio. The flex split, border, padding, and hover behavior stay intact unless you also override them.
Yes. The Card root renders children directly, so you can add a third div between Card.Preview and Card.Info, or compose multiple Card.Info rows. The default vertical flex layout stacks every direct child, so any number of regions render top-to-bottom. If you go beyond three regions, consider whether shadcn/ui's Card with its dedicated header / content / footer slots fits the editorial layout better.
Wrap the Card in a Next.js Link with className="block". The Link makes the entire Card clickable and inherits routing prefetch behavior. Avoid placing buttons or other interactive elements inside a Linked Card — nested interactives break keyboard tab order. If you need a card with both navigation and a button action, render the Link as the title inside Card.Info instead of wrapping the whole tile.
The 20px radius lands between Tailwind's rounded-2xl (16px) and rounded-3xl (24px) — close enough that neither preset fits exactly. The arbitrary value pins the shape Drivn ships across every example. To change it, edit the base entry of the styles object in your local copy of the Card source after installing — the Drivn philosophy is "copy and own", so the file is yours to modify.