Drivn vs shadcn/ui — Card Component Compared
Drivn and shadcn/ui Card components compared — dot notation vs six named exports, built-in hover lift, fixed showcase sizing, and zero runtime UI deps.
Pick up shadcn/ui's Card and you get a blank rectangle with six named exports — CardHeader, CardTitle, CardDescription, CardContent, CardFooter, and CardAction — arranged however your layout needs. Pick up Drivn's and you get two slots, Card.Preview and Card.Info, hung off the root with Object.assign, plus a fixed w-48 aspect-square footprint already baked into the base class string. Same primitive, opposite philosophy about who makes the layout decisions.
Drivn's narrowness is deliberate. The Card exists for the tile that repeats across component galleries, plugin marketplaces, design system docs, and product grids: artwork or an icon filling the top region, a title row pinned underneath, and a small hover lift telling the reader the tile is clickable. Encoding that shape in the component means every card in the grid lines up without a single utility class at the call site. shadcn encodes nothing, so every card is yours to size.
What follows compares the two on the axes that actually change your code: how many imports each usage costs, what the defaults hand you for free, how hover is expressed, and which layouts each shape stops fitting. Every snippet below is read straight from the Card source the Drivn CLI drops into your repo.
Side-by-side comparison
| Feature | Drivn | shadcn/ui |
|---|---|---|
| API style | Dot notation (Card.Preview, Card.Info) | Six named exports (CardHeader, CardTitle, …) |
| Sub-components | 2 slots | 6 components |
| Default size | w-48 aspect-square (fixed) | Fluid, full-width |
| Built-in hover lift | hover prop (default true) | Add classes manually |
| Runtime UI deps | None | None |
| Border radius | rounded-[20px] | rounded-xl |
| Preview / info layout | Built-in flex split | Compose with utility classes |
| TypeScript | Required | Required |
| License | MIT | MIT |
| Copy-paste install |
Import surface
shadcn/ui exports six named components from one file: Card, CardHeader, CardTitle, CardDescription, CardContent, and CardFooter. Each one is a styled div, and you compose them in the order the layout needs. Drivn exports a single object — Card — with two children mounted on it: Card.Preview and Card.Info. One import line covers the whole component, and the dot access lets a reader see at a glance which div is the visual area and which is the text area.
The trade-off is reach. shadcn's six exports cover blog cards, profile cards, dashboard widgets, settings panels — anything where a header, body, and footer compose differently. Drivn's two slots cover the pattern Drivn cares about: a square showcase tile with an illustration on top and a label on the bottom. If your card has a header bar, body paragraph, and footer button row, shadcn fits better. If your card is a gallery item or component preview, Drivn writes in fewer lines. Pick the one that matches the shape you actually render. The full Card docs cover both slots in detail.
1 // shadcn/ui — six named exports 2 import { 3 Card, 4 CardHeader, 5 CardTitle, 6 CardDescription, 7 CardContent, 8 CardFooter, 9 } from '@/components/ui/card' 10 11 // Drivn — one import, dot notation 12 import { Card } from '@/components/ui/card'
Sizing and layout defaults
Drivn's Card carries a fixed w-48 aspect-square footprint plus a vertical flex layout where Card.Preview claims flex-1 and Card.Info sits below it with p-5 padding and a horizontal flex split. That makes a Drivn Card a 192px × 192px tile by default, with the preview filling whatever vertical space the info row leaves. shadcn/ui's Card has no width or height — it stretches to fill its parent and stacks children vertically with a gap-6 between them.
When you need a different tile size in Drivn, override the width with a className on the root: <Card className="w-64">…</Card>. The aspect-square stays in effect unless you also override it. shadcn requires no override because it has no defaults to fight, but the cost is more utility classes per usage to lock in a consistent grid. For a gallery of equal-sized tiles, Drivn's defaults save a grid template column dance. For a settings panel, shadcn's flexibility wins. The Card examples page shows the gallery pattern in full.
1 // Drivn — fixed showcase tile, vertical flex split 2 <Card> 3 <Card.Preview> 4 <span className="text-2xl">★</span> 5 </Card.Preview> 6 <Card.Info> 7 <p className="text-sm font-semibold">Card title</p> 8 </Card.Info> 9 </Card> 10 11 // shadcn/ui — fluid width, six children 12 <Card> 13 <CardHeader> 14 <CardTitle>Title</CardTitle> 15 <CardDescription>Description</CardDescription> 16 </CardHeader> 17 <CardContent>Body</CardContent> 18 <CardFooter>Footer</CardFooter> 19 </Card>
Hover behavior
Hover is a prop in Drivn and a class list in shadcn. The Drivn base string always carries transition-all duration-200, and the root composes cn(styles.base, hover && styles.hover, className) — so the styles.hover key, hover:bg-accent hover:border-border hover:-translate-y-1, only lands when the prop is on. It defaults to true. The -translate-y-1 is the piece readers actually notice: a small rise that reads as "this tile responds". Passing hover={false} drops all three classes but leaves the transition in place, which matters if you animate something else on the same element.
shadcn/ui's Card ships no transition and no hover state, so a card inside a link behaves exactly like a card inside a static marketing block until you say otherwise. Neither default is wrong — Drivn assumes the gallery context where every tile is clickable, shadcn assumes nothing. Recreating the Drivn feel in shadcn means copying those same three classes onto your Card; going the other direction is one prop. The Card examples page shows the grid this behavior was tuned for.
1 // Drivn — hover lift built in (toggle off with prop) 2 <Card hover={false}> 3 <Card.Preview>Static preview</Card.Preview> 4 <Card.Info>No hover lift</Card.Info> 5 </Card> 6 7 // shadcn/ui — hover styling per usage 8 <Card className="transition-all hover:bg-accent hover:-translate-y-1"> 9 <CardHeader> 10 <CardTitle>Hover lift</CardTitle> 11 </CardHeader> 12 </Card>
When each one fits
Reach for shadcn/ui's Card when the layout has a meaningful header / body / footer split — settings panels, blog post previews, dashboard metric cards with an action bar, profile summaries with avatar plus stats. The six-export surface is exactly the shape those layouts need, and the lack of size defaults lets the card flex to whatever container holds it.
Reach for Drivn's Card when you are rendering a uniform grid of square tiles with an illustration up top and a label below — component galleries, plugin marketplaces, product card grids, design system documentation. Drivn's defaults take the design decisions out of your hands so the grid stays visually consistent across every usage. If you find yourself overriding w-, aspect-, and hover: classes on every shadcn Card to land at the same showcase shape, switch. If you find yourself fighting the w-48 aspect-square default on every Drivn Card to make it stretch, switch back. The Drivn CLI installs the source either way, so the migration is a copy and replace.
1 // Drivn — fits a square showcase grid 2 <div className="grid grid-cols-3 gap-4"> 3 {items.map((item) => ( 4 <Card key={item.id}> 5 <Card.Preview>{item.icon}</Card.Preview> 6 <Card.Info> 7 <p className="text-sm font-semibold">{item.name}</p> 8 </Card.Info> 9 </Card> 10 ))} 11 </div>
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
Drivn's Card is built for showcase grids — component galleries, plugin marketplaces, design system docs — where a square tile with an illustration on top and a label on the bottom is the dominant pattern. Two slots cover that shape with the fewest moving parts. If you need a richer card with header, body, and footer rows, override the slots with utility classes or use shadcn/ui's Card, which is tuned for that case.
Yes. The className prop on the Card root merges with the base classes via the cn utility, so passing <Card className="w-full aspect-auto"> overrides both width and aspect ratio. The flex layout, border, and rounded corners stay intact unless you also override them. The fixed defaults exist for the gallery use case — they are a starting point, not a constraint.
No. The component is plain React plus Tailwind. The source imports React and a cn class merger and that is it — no Radix slot, no cva, no floating-ui. Same is true of shadcn/ui's Card on this axis. Both pages render to ordinary divs, and the only difference at runtime is the tree shape and which classes you wrote.
Map shadcn's CardHeader plus CardContent onto Drivn's Card.Preview, and CardFooter onto Card.Info. Keep your existing className overrides; they merge through Drivn's cn utility the same way. If your card has more than two regions, fold the extra rows into either slot with utility classes — the slot is a div, so any flex or grid arrangement works inside it. The migration is mechanical for showcase tiles and harder for editorial layouts where shadcn's six-slot model is the better fit.

