Hover Card
A preview that opens while the pointer rests on a link: the profile behind a mention, the page behind a link, the commit behind a hash. Popover's surface, grown out of the link it previews.
Installation#
Usage#
import {
HoverCard,
HoverCardTrigger,
HoverCardContent,
} from "@/components/ui/hover-card"
export function Example() {
return (
<HoverCard>
<HoverCardTrigger asChild>
<a href="/people/katiep">@katiep</a>
</HoverCardTrigger>
<HoverCardContent>Katie Park, Design Systems</HoverCardContent>
</HoverCard>
)
}Link preview#
The page behind a link. HoverCardMedia sets the picture 8px in from the card's edges instead of bleeding to them, with concentric corners (the card's 16px radius minus 8px of inset leaves 8px on the picture). The copy under it keeps the card's own 16px. It is aspect-video until you give it another ratio.
Commit preview#
A reference in running text, opened into what it points at: the subject, the author, and the size of the change. The counts sit in HoverCardFooter, a muted well set in from the card's edges like the media, where a divider line would otherwise go. They are tabular-nums, so a column of them lines up. align="start" opens the card from the hash's left edge.
Placement#
side, align and sideOffset are Radix's, and the card flips when the side you asked for has no room. showArrow adds a pointer toward the link. The card grows out of the trigger it previews, from Radix's transform origin, rather than from its own centre. These open after 150ms so you can sweep across them.
Timing#
The card waits openDelay (700ms) before it opens, so a pointer passing over a paragraph full of links doesn't set off a string of cards, and closeDelay (300ms) after the pointer leaves, which is the time it has to cross the gap into the card. Both are props on HoverCard. Shorten the open delay where the preview is the point (a list of people), and keep the close delay: without it the card vanishes on the way to it.
Accessibility#
A hover card is for sighted pointer users. It opens when the trigger has focus, but the card never takes focus and it never opens on touch, so everything in it must also be reachable where the link goes. Make the trigger a real link to that place, and never put the only way to do something inside the card. When the content has to be operated, use a Popover, which opens on a click and moves focus into itself.
API reference#
HoverCard
The root, no DOM. Props: openDelay (700), closeDelay (300), open, defaultOpen, onOpenChange.
HoverCardTrigger
Renders an <a>; pass asChild to use your own link (a Link, a Next.js link).
HoverCardContent
The card, portalled to the body. Props: side, align (center), sideOffset (8), showArrow, density (comfortable 16px padding · compact 12px), className. It declares --surface, so a nested control blends with the card.
HoverCardTitle · HoverCardDescription
Optional type for the card: an <h4> title and a muted <p>, the same pair Popover uses.
HoverCardMedia · HoverCardFooter
The inset parts, Popover's own. HoverCardMedia frames one <img> or <video> (aspect-video, override with className), first or last in the card. HoverCardFooter is a muted flex row that always closes the card. Both sit 8px in from the edges (4px at compact) with a concentric radius, and neither takes a density of its own.