Skip to main content
The Preview Card component shows a popup with additional content when hovering over or focusing on a trigger element, similar to a rich tooltip or popover.

Installation

npx shadcn@latest add @eo-n/preview-card

Usage

import {
  PreviewCard,
  PreviewCardContent,
  PreviewCardTrigger,
} from "@/components/ui/preview-card";
<PreviewCard>
  <PreviewCardTrigger>Hover me</PreviewCardTrigger>
  <PreviewCardContent>
    This is the preview content.
  </PreviewCardContent>
</PreviewCard>

Examples

Basic Preview Card

import {
  PreviewCard,
  PreviewCardContent,
  PreviewCardTrigger,
} from "@/components/ui/preview-card";

<PreviewCard>
  <PreviewCardTrigger asChild>
    <button>Hover to preview</button>
  </PreviewCardTrigger>
  <PreviewCardContent>
    <p>Innovating in reverse.</p>
  </PreviewCardContent>
</PreviewCard>

Rich Content Preview

import {
  PreviewCard,
  PreviewCardContent,
  PreviewCardTrigger,
} from "@/components/ui/preview-card";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";

<PreviewCard>
  <PreviewCardTrigger asChild>
    <a href="#" className="font-medium underline">
      @username
    </a>
  </PreviewCardTrigger>
  <PreviewCardContent>
    <div className="flex items-start gap-3">
      <Avatar>
        <AvatarImage src="https://github.com/aeonzz.png" />
        <AvatarFallback>UN</AvatarFallback>
      </Avatar>
      <div className="space-y-1">
        <h4 className="text-sm font-semibold">Username</h4>
        <p className="text-sm text-muted-foreground">
          Full-stack developer and open source enthusiast.
        </p>
        <div className="flex gap-2 text-xs text-muted-foreground">
          <span>1.2k followers</span>
          <span></span>
          <span>340 following</span>
        </div>
      </div>
    </div>
  </PreviewCardContent>
</PreviewCard>
import {
  PreviewCard,
  PreviewCardContent,
  PreviewCardTrigger,
} from "@/components/ui/preview-card";

<PreviewCard>
  <PreviewCardTrigger asChild>
    <a href="https://example.com" className="text-primary hover:underline">
      View Documentation
    </a>
  </PreviewCardTrigger>
  <PreviewCardContent className="w-80">
    <div className="space-y-2">
      <img
        src="/docs-preview.png"
        alt="Documentation preview"
        className="w-full rounded-md"
      />
      <h3 className="font-semibold">Getting Started Guide</h3>
      <p className="text-sm text-muted-foreground">
        Learn how to set up and use our platform in just a few minutes.
      </p>
    </div>
  </PreviewCardContent>
</PreviewCard>

Product Preview

import {
  PreviewCard,
  PreviewCardContent,
  PreviewCardTrigger,
} from "@/components/ui/preview-card";
import { Badge } from "@/components/ui/badge";

<PreviewCard>
  <PreviewCardTrigger asChild>
    <button className="text-left">
      Premium Plan
    </button>
  </PreviewCardTrigger>
  <PreviewCardContent>
    <div className="space-y-3">
      <div className="flex items-center justify-between">
        <h3 className="font-semibold">Premium</h3>
        <Badge>Popular</Badge>
      </div>
      <div className="text-2xl font-bold">$29/month</div>
      <ul className="space-y-1 text-sm text-muted-foreground">
        <li>✓ Unlimited projects</li>
        <li>✓ Priority support</li>
        <li>✓ Advanced analytics</li>
        <li>✓ Custom domains</li>
      </ul>
    </div>
  </PreviewCardContent>
</PreviewCard>

Custom Positioning

<PreviewCard>
  <PreviewCardTrigger>Hover me</PreviewCardTrigger>
  <PreviewCardContent 
    side="top" 
    sideOffset={8}
    align="start"
  >
    This preview opens above the trigger.
  </PreviewCardContent>
</PreviewCard>

Component API

PreviewCard

The root container component.
open
boolean
Controlled open state.
defaultOpen
boolean
Default open state for uncontrolled usage.
onOpenChange
(open: boolean) => void
Callback when the open state changes.
openDelay
number
Delay in milliseconds before opening. Defaults to 600.
closeDelay
number
Delay in milliseconds before closing. Defaults to 300.

PreviewCardTrigger

The element that triggers the preview.
asChild
boolean
When true, merges props into the immediate child element instead of rendering a button.
children
React.ReactNode
required
The trigger content.

PreviewCardContent

The preview content container.
side
'top' | 'right' | 'bottom' | 'left'
The preferred side of the trigger to render against.
sideOffset
number
Distance in pixels from the trigger. Defaults to 4.
align
'start' | 'center' | 'end'
The alignment of the preview relative to the trigger.
alignOffset
number
Offset in pixels from the alignment axis.
collisionBoundary
Element | Element[]
Elements to check for collision.
collisionPadding
number | Partial<Record<'top' | 'right' | 'bottom' | 'left', number>>
Padding from the collision boundary.
className
string
Additional CSS classes.
children
React.ReactNode
required
The preview content.

Accessibility

The Preview Card component includes:
  • Keyboard navigation support (opens on focus)
  • Screen reader announcements
  • Focus management
  • ESC key to close
  • Proper ARIA attributes

Reference

Built on top of Base UI Preview Card.

Build docs developers (and LLMs) love