1
ComponentsOverlays

Preview Card

Optional profile, repository, term or product previews shown from a hoverable and focusable trigger.

Purpose

Use Preview Card for optional supporting previews such as profiles, repositories, terms or products that appear from a hoverable and focusable trigger.

Hover preview

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

export function Example() {
  return (
    <PreviewCard>
      <PreviewCardTrigger render={<Button variant="link" />}>Hover project</PreviewCardTrigger>
      <PreviewCardPopup>Project summary</PreviewCardPopup>
    </PreviewCard>
  );
}

Installation

With the Siteplane registry alias configured, add the component and import the installed source from your app.

CLI

pnpm dlx shadcn@4.16.1 add @siteplane/preview-card

Import

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

Need the machine-readable source metadata? View registry JSON.

Usage

Preview Card reveals contextual preview content on hover or focus without changing the current page. Keep the trigger a real link so keyboard users can open it too.

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

export function Example() {
  return (
    <PreviewCard>
      <PreviewCardTrigger render={<Button variant="link" />}>Hover project</PreviewCardTrigger>
      <PreviewCardPopup>Project summary</PreviewCardPopup>
    </PreviewCard>
  );
}

Examples

Rich popup content, trigger elements, width overrides and popup positioning are compositions and props of the same primitive.

User card

<PreviewCard>
  <PreviewCardTrigger href="#user">@lina</PreviewCardTrigger>
  <PreviewCardPopup>
    <div className="grid gap-3">
      <div className="flex items-start gap-3">
        <Avatar className="size-10">
          <AvatarFallback>LV</AvatarFallback>
        </Avatar>
        <div className="grid gap-0.5">
          <p className="font-medium text-sm leading-5">Lina Vogel</p>
          <p className="text-muted-foreground text-xs leading-5">@lina</p>
        </div>
      </div>
      <p className="text-muted-foreground text-xs leading-5">
        Product designer on the Siteplane UI core team.
      </p>
      <Button aria-label="Follow Lina Vogel" className="w-full" type="button">
        <UserPlusIcon />
        Follow
      </Button>
    </div>
  </PreviewCardPopup>
</PreviewCard>

Repo card

<PreviewCard>
  <PreviewCardTrigger href="#repo">siteplane/ui</PreviewCardTrigger>
  <PreviewCardPopup>
    <div className="grid gap-2">
      <div className="grid gap-0.5">
        <p className="font-medium text-sm leading-5">siteplane/ui</p>
        <p className="text-muted-foreground text-xs leading-5">
          Siteplane UI primitives on Base UI and Tailwind.
        </p>
      </div>
      <div className="flex items-center gap-4 text-muted-foreground text-xs">
        <span className="flex items-center gap-1"><StarIcon className="size-3.5" />1.2k</span>
        <span className="flex items-center gap-1"><GitForkIcon className="size-3.5" />184</span>
      </div>
    </div>
  </PreviewCardPopup>
</PreviewCard>

Custom trigger

Beta
<PreviewCard>
  <PreviewCardTrigger href="#beta" render={<Badge variant="outline">Beta</Badge>} />
  <PreviewCardPopup>
    <p className="text-sm leading-5">Beta features are enabled and may still change.</p>
  </PreviewCardPopup>
</PreviewCard>

Custom width

{/* Overrides the default w-64 popup width */}
<PreviewCardPopup className="w-56">
  <div className="flex items-center gap-2.5">
    <Avatar className="size-7">
      <AvatarFallback>MK</AvatarFallback>
    </Avatar>
    <div className="grid gap-0.5">
      <p className="truncate font-medium text-sm leading-5">Max Keller</p>
      <p className="truncate text-muted-foreground text-xs leading-5">Online 5 minutes ago</p>
    </div>
  </div>
</PreviewCardPopup>

Alignment

<PreviewCardPopup align="start" sideOffset={8}>...</PreviewCardPopup>
<PreviewCardPopup align="center" sideOffset={8}>...</PreviewCardPopup>
<PreviewCardPopup align="end" sideOffset={8}>...</PreviewCardPopup>

API Reference

The reference lists the source-owned exports and props that are easy to miss in visual examples. Inherited Base UI props remain available unless the wrapper narrows them.

  • Compose PreviewCard, PreviewCardTrigger and PreviewCardPopup.
  • PreviewCardTrigger renders a link by default and accepts Base UI's render prop for another interactive element.
  • PreviewCardPopup accepts placement props including side, align, sideOffset and anchor; anchor can point at a different element or virtual position.
  • Keep popup content compact and non-essential because it is supplementary to the trigger's destination.

Motion

Component motion uses the shared Siteplane motion contract. See the global motion guide for provider setup, tokens and reduced-motion behavior.

  • The popup uses the native Siteplane floating-panel scale and opacity transition.
  • Open delay, close behavior and reduced motion remain controlled by the Preview Card primitive and global motion layer.

Accessibility

  • The trigger must be an interactive element with its own accessible name and keyboard focus.
  • The preview opens from both hover and focus; preserve Escape behavior.
  • Images need useful alt text or a text fallback, and any popup action needs a clear accessible name.

Implementation Guidance

  • Never put required instructions, validation or critical status only inside a Preview Card.
  • Do not render a popup without an interactive trigger or a deliberate anchor relationship.
  • Use Popover instead when the floating content itself is the primary interactive task.

On This Page