Preview Link Card

A link that shows a screenshot of the page it leads to when hovered, in a card that links there too. Built on Base UI's Preview Card.

Read the Animate UI Docs — hover to preview, click to dive in.

"use client"

import { cn } from "cn"
import {
  PreviewLinkCard as PreviewLinkCardPrimitive,
  PreviewLinkCardImage as PreviewLinkCardImagePrimitive,
  PreviewLinkCardPopup as PreviewLinkCardPopupPrimitive,
  PreviewLinkCardPortal as PreviewLinkCardPortalPrimitive,
  PreviewLinkCardPositioner as PreviewLinkCardPositionerPrimitive,
  PreviewLinkCardTrigger as PreviewLinkCardTriggerPrimitive,
  type PreviewLinkCardImageProps,
  type PreviewLinkCardPopupProps,
  type PreviewLinkCardPositionerProps,
  type PreviewLinkCardProps,
  type PreviewLinkCardTriggerProps,
} from "@animate-ui/react/primitives/base-ui/preview-link-card"

function PreviewLinkCard(props: PreviewLinkCardProps) {
  return <PreviewLinkCardPrimitive {...props} />
}

function PreviewLinkCardTrigger(props: PreviewLinkCardTriggerProps) {
  return <PreviewLinkCardTriggerPrimitive {...props} />
}

function PreviewLinkCardPanel({
  className,
  side = "top",
  sideOffset = 4,
  align = "center",
  alignOffset = 0,
  ...props
}: PreviewLinkCardPopupProps &
  Pick<
    PreviewLinkCardPositionerProps,
    "align" | "alignOffset" | "side" | "sideOffset"
  >) {
  return (
    <PreviewLinkCardPortalPrimitive>
      <PreviewLinkCardPositionerPrimitive
        align={align}
        alignOffset={alignOffset}
        side={side}
        sideOffset={sideOffset}
        className="isolate z-50"
      >
        <PreviewLinkCardPopupPrimitive
          className={cn(
            "ring-foreground/10 bg-popover z-50 origin-(--transform-origin) overflow-hidden rounded-lg shadow-md ring-1 outline-hidden",
            className
          )}
          {...props}
        />
      </PreviewLinkCardPositionerPrimitive>
    </PreviewLinkCardPortalPrimitive>
  )
}

function PreviewLinkCardImage({
  className,
  ...props
}: PreviewLinkCardImageProps) {
  return (
    <PreviewLinkCardImagePrimitive
      className={cn("block", className)}
      {...props}
    />
  )
}

export {
  PreviewLinkCard,
  PreviewLinkCardTrigger,
  PreviewLinkCardPanel,
  PreviewLinkCardImage,
}

export default function PreviewLinkCardDemo({
  followCursor = false,
  href = "https://animate-ui.com/docs",
  side = "top",
  sideOffset = 4,
  align = "center",
  alignOffset = 0,
}: {
  followCursor?: boolean | "x" | "y"
  href?: string
  side?: "top" | "right" | "bottom" | "left" | "inline-start" | "inline-end"
  sideOffset?: number
  align?: "start" | "center" | "end"
  alignOffset?: number
}) {
  return (
    <p className="text-muted-foreground">
      Read the{" "}
      <PreviewLinkCard href={href} followCursor={followCursor}>
        <PreviewLinkCardTrigger
          target="_blank"
          className="text-foreground underline"
        >
          Animate UI Docs
        </PreviewLinkCardTrigger>
        <PreviewLinkCardPanel
          side={side}
          sideOffset={sideOffset}
          align={align}
          alignOffset={alignOffset}
          target="_blank"
        >
          <PreviewLinkCardImage alt="Animate UI Docs" />
        </PreviewLinkCardPanel>
      </PreviewLinkCard>{" "}
      — hover to preview, click to dive in.
    </p>
  )
}

Installation

npm install @animate-ui/react motion
pnpm add @animate-ui/react motion
yarn add @animate-ui/react motion
bun add @animate-ui/react motion

It's built on Base UI's Preview Card, a peer dependency of this primitive:

npm install @base-ui/react
pnpm add @base-ui/react
yarn add @base-ui/react
bun add @base-ui/react

Usage

import {
  PreviewLinkCard,
  PreviewLinkCardImage,
  PreviewLinkCardPopup,
  PreviewLinkCardPortal,
  PreviewLinkCardPositioner,
  PreviewLinkCardTrigger,
} from '@animate-ui/react/primitives/base-ui/preview-link-card';

export function DocsLink() {
  return (
    <PreviewLinkCard href="https://animate-ui.com/docs">
      <PreviewLinkCardTrigger>Animate UI Docs</PreviewLinkCardTrigger>
      <PreviewLinkCardPortal>
        <PreviewLinkCardPositioner>
          <PreviewLinkCardPopup>
            <PreviewLinkCardImage alt="Animate UI Docs" />
          </PreviewLinkCardPopup>
        </PreviewLinkCardPositioner>
      </PreviewLinkCardPortal>
    </PreviewLinkCard>
  );
}

The screenshot comes from Microlink's API, as in Animate UI v1: it captures href on a mobile viewport three times the image's size. Pass src to show an image of your own instead. Either way, it's preloaded (a <link rel="preload"> in the head) as soon as the card mounts, so it's there when the card opens.

Animation

It is Animate UI's Preview Card, so the card moves exactly like it:

  • Popup grows from 0.8 and fades in, from the trigger (--transform-origin), on a spring (stiffness 400, damping 29) that overshoots a little before it settles.
  • It opens after the trigger's delay (600ms by default) and, once its closeDelay is over (300ms), closes the same way: it shrinks back to 0.8 on the same spring while it fades out, then unmounts.
  • Reopening while it closes picks up from where it is, on the same spring.
  • Base UI only opens it on hover for mouse-like pointers: nothing sticks on touch screens.
  • With followCursor on the Root, the card leans towards the pointer while it moves over the trigger, on a spring (stiffness 200, damping 17), as in v1.
  • Under reduced motion, it only fades (no scale).

API Reference

The parts take the props of Animate UI's Preview Card, so those of Base UI's Preview Card, plus the ones below.

PreviewLinkCard

Preview Card's Root (open, onOpenChange, followCursor…), plus:

PropTypeDefault
href
string-
src?
string-
width?
number240
height?
number135
deviceScaleFactor?
number1
colorScheme?
'light' | 'dark''light'

PreviewLinkCardTrigger

Preview Card's Trigger (delay, closeDelay…): a link, to the Root's href unless it has its own.

PreviewLinkCardPortal, PreviewLinkCardBackdrop, PreviewLinkCardArrow

Preview Card's Portal, Backdrop and Arrow.

PreviewLinkCardPositioner

Preview Card's Positioner, with v1's defaults:

PropTypeDefault
side?
'top' | 'right' | 'bottom' | 'left' | 'inline-start' | 'inline-end''top'
sideOffset?
number10
align?
'start' | 'center' | 'end''center'

PreviewLinkCardPopup

Preview Card's Popup (transition, exitTransition…), rendered as a link to the Root's href: it is the card, and its className styles it. A render of your own replaces the link (and is made animatable).

PropTypeDefault
href?
string-
target, rel, download, hrefLang, ping, referrerPolicy?
React.ComponentProps<'a'>-

PreviewLinkCardImage

PropTypeDefault
alt?
string'preview image'
...props?
Omit<React.ComponentProps<'img'>, 'src' | 'width' | 'height'>-

usePreviewLinkCard

Returns the card's href, its image's src, width and height: for an image of your own.

Credits

Built by Skyleen. The source code is available on GitHub.

Last updated: 10/18/2026

On this page