Github

Hover Card

For sighted users to preview content available behind a link.

"use client";

import {
	Avatar,
	AvatarFallback,
	AvatarImage,
} from "@/components/ui/avatar";
import { Button } from "@/components/ui/button";
import {
	HoverCard,
	HoverCardContent,
	HoverCardTrigger,
} from "@/components/ui/hover-card";

export function HoverCardDemo() {
	return (
		<HoverCard>
			<HoverCardTrigger
				delay={10}
				closeDelay={100}
				render={<Button variant="link" />}
			>
				@nextjs
			</HoverCardTrigger>
			<HoverCardContent className="w-64">
				<div className="flex items-center gap-3">
					<Avatar>
						<AvatarImage src="https://github.com/vercel.png" />
						<AvatarFallback>VC</AvatarFallback>
					</Avatar>
					<div className="flex flex-col gap-0.5">
						<div className="font-semibold">@nextjs</div>
						<div className="text-muted-foreground text-xs">
							The React Framework
						</div>
					</div>
				</div>
				<div className="text-muted-foreground text-xs">
					Created and maintained by @vercel.
				</div>
				<div className="text-muted-foreground text-xs">
					Joined December 2021
				</div>
			</HoverCardContent>
		</HoverCard>
	);
}

Installation

pnpm dlx shadcn@latest add @herocn/hover-card

Usage

import {
  HoverCard,
  HoverCardContent,
  HoverCardTrigger,
} from "@/components/ui/hover-card"
<HoverCard>
  <HoverCardTrigger>Hover</HoverCardTrigger>
  <HoverCardContent>
    The React Framework – created and maintained by @vercel.
  </HoverCardContent>
</HoverCard>

Composition

Use the following composition to build a HoverCard:

HoverCard
├── HoverCardTrigger
└── HoverCardContent

Trigger Delays

Use delay and closeDelay on the trigger to control when the card opens and closes.

<HoverCard>
  <HoverCardTrigger delay={100} closeDelay={200}>
    Hover
  </HoverCardTrigger>
  <HoverCardContent>Content</HoverCardContent>
</HoverCard>

Positioning

Use the side and align props on HoverCardContent to control placement.

<HoverCard>
  <HoverCardTrigger>Hover</HoverCardTrigger>
  <HoverCardContent side="top" align="start">
    Content
  </HoverCardContent>
</HoverCard>

Examples

Sides

Use the side prop to change the position of the hover card.

"use client";

import { Button } from "@/components/ui/button";
import {
	HoverCard,
	HoverCardContent,
	HoverCardTrigger,
} from "@/components/ui/hover-card";

export function HoverCardSides() {
	return (
		<div className="flex flex-wrap gap-2">
			{(["left", "top", "bottom", "right"] as const).map((side) => (
				<HoverCard key={side}>
					<HoverCardTrigger
						delay={100}
						closeDelay={100}
						render={<Button variant="tertiary" className="w-fit capitalize" />}
					>
						{side}
					</HoverCardTrigger>
					<HoverCardContent side={side}>
						<div className="flex flex-col gap-1">
							<h4 className="font-medium">Hover Card</h4>
							<p className="text-muted-foreground text-sm">
								This hover card appears on the {side} side of the trigger.
							</p>
						</div>
					</HoverCardContent>
				</HoverCard>
			))}
		</div>
	);
}

With arrow

Use the withArrow prop to add an arrow pointing at the trigger.

"use client";

import { Button } from "@/components/ui/button";
import {
	HoverCard,
	HoverCardContent,
	HoverCardTrigger,
} from "@/components/ui/hover-card";

export function HoverCardWithArrow() {
	return (
		<div className="flex flex-wrap gap-2">
			{(["left", "top", "bottom", "right"] as const).map((side) => (
				<HoverCard key={side}>
					<HoverCardTrigger
						delay={100}
						closeDelay={100}
						render={<Button variant="tertiary" className="w-fit capitalize" />}
					>
						{side}
					</HoverCardTrigger>
					<HoverCardContent withArrow side={side}>
						<div className="flex flex-col gap-1">
							<h4 className="font-medium">Hover Card</h4>
							<p className="text-muted-foreground text-sm">
								This hover card has an arrow pointing at the trigger.
							</p>
						</div>
					</HoverCardContent>
				</HoverCard>
			))}
		</div>
	);
}

RTL

To enable RTL support in shadcn/ui, see the RTL configuration guide.

"use client";

import {
	type Translations,
	useTranslation,
} from "@/components/language-selector";
import { Button } from "@/components/ui/button";
import {
	HoverCard,
	HoverCardContent,
	HoverCardTrigger,
} from "@/components/ui/hover-card";

const translations: Translations = {
	en: {
		dir: "ltr",
		values: {
			content: "The React Framework – created and maintained by @vercel.",
			"inline-start": "Inline Start",
			left: "Left",
			top: "Top",
			bottom: "Bottom",
			right: "Right",
			"inline-end": "Inline End",
		},
	},
	ar: {
		dir: "rtl",
		values: {
			content: "إطار عمل React - تم إنشاؤه وصيانته بواسطة @vercel.",
			"inline-start": "بداية السطر",
			left: "يسار",
			top: "أعلى",
			bottom: "أسفل",
			right: "يمين",
			"inline-end": "نهاية السطر",
		},
	},
	he: {
		dir: "rtl",
		values: {
			content: "React Framework - נוצר ומתוחזק על ידי @vercel.",
			"inline-start": "תחילת השורה",
			left: "שמאל",
			top: "למעלה",
			bottom: "למטה",
			right: "ימין",
			"inline-end": "סוף השורה",
		},
	},
};

const physicalSides = ["left", "top", "bottom", "right"] as const;
const logicalSides = ["inline-start", "inline-end"] as const;

export function HoverCardRtl() {
	const { dir, language, t } = useTranslation(translations, "ar");

	return (
		<div lang={language} dir={dir} className="grid gap-4">
			<div className="flex flex-wrap justify-center gap-2">
				{physicalSides.map((side) => (
					<HoverCard key={side}>
						<HoverCardTrigger
							delay={10}
							closeDelay={100}
							render={<Button variant="tertiary" />}
						>
							{t[side]}
						</HoverCardTrigger>
						<HoverCardContent side={side} dir={dir} className="w-64">
							<div className="font-semibold">@nextjs</div>
							<div className="text-muted-foreground text-sm">{t.content}</div>
						</HoverCardContent>
					</HoverCard>
				))}
			</div>
			<div className="flex flex-wrap justify-center gap-2">
				{logicalSides.map((side) => (
					<HoverCard key={side}>
						<HoverCardTrigger
							delay={10}
							closeDelay={100}
							render={<Button variant="tertiary" />}
						>
							{t[side]}
						</HoverCardTrigger>
						<HoverCardContent side={side} dir={dir} className="w-64">
							<div className="font-semibold">@nextjs</div>
							<div className="text-muted-foreground text-sm">{t.content}</div>
						</HoverCardContent>
					</HoverCard>
				))}
			</div>
		</div>
	);
}

API Reference

The hover card is built on Base UI PreviewCard. HoverCardContent sets default positioning and passes the remaining props to the popup.

HoverCardContent

PropTypeDefault

See the Base UI PreviewCard API reference for root, trigger, and other props.