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 Radix UI's Hover Card.

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

"use client"

import { cn } from "cn"
import {
  PreviewLinkCard as PreviewLinkCardPrimitive,
  PreviewLinkCardContent as PreviewLinkCardContentPrimitive,
  PreviewLinkCardImage as PreviewLinkCardImagePrimitive,
  PreviewLinkCardPortal as PreviewLinkCardPortalPrimitive,
  PreviewLinkCardTrigger as PreviewLinkCardTriggerPrimitive,
  type PreviewLinkCardContentProps,
  type PreviewLinkCardImageProps,
  type PreviewLinkCardProps,
  type PreviewLinkCardTriggerProps,
} from "@animate-ui/react/primitives/radix-ui/preview-link-card"

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

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

function PreviewLinkCardContent({
  className,
  align = "center",
  sideOffset = 4,
  ...props
}: PreviewLinkCardContentProps) {
  return (
    <PreviewLinkCardPortalPrimitive>
      <PreviewLinkCardContentPrimitive
        align={align}
        sideOffset={sideOffset}
        className={cn(
          "ring-foreground/10 bg-popover z-50 origin-(--radix-hover-card-content-transform-origin) overflow-hidden rounded-lg shadow-md ring-1 outline-hidden",
          className
        )}
        {...props}
      />
    </PreviewLinkCardPortalPrimitive>
  )
}

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

export {
  PreviewLinkCard,
  PreviewLinkCardTrigger,
  PreviewLinkCardContent,
  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"
  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>
        <PreviewLinkCardContent
          side={side}
          sideOffset={sideOffset}
          align={align}
          alignOffset={alignOffset}
          target="_blank"
        >
          <PreviewLinkCardImage alt="Animate UI Docs" />
        </PreviewLinkCardContent>
      </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 Radix UI's Hover Card, a peer dependency of this primitive:

npm install radix-ui
pnpm add radix-ui
yarn add radix-ui
bun add radix-ui

Usage

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

export function DocsLink() {
  return (
    <PreviewLinkCard href="https://animate-ui.com/docs">
      <PreviewLinkCardTrigger>Animate UI Docs</PreviewLinkCardTrigger>
      <PreviewLinkCardPortal>
        <PreviewLinkCardContent>
          <PreviewLinkCardImage alt="Animate UI Docs" />
        </PreviewLinkCardContent>
      </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 Hover Card, so the card moves exactly like it:

  • Content grows from 0.8 and fades in, from the trigger (--radix-hover-card-content-transform-origin), on a spring (stiffness 400, damping 29) that overshoots a little before it settles.
  • It opens after Radix's openDelay (700ms 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.
  • Radix only opens it for mouse-like pointers: a tap on a touch screen follows the link, nothing sticks.
  • 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 Hover Card, so those of Radix UI's Hover Card, plus the ones below.

PreviewLinkCard

Hover Card's Root (openDelay, closeDelay, open, followCursor…), plus:

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

PreviewLinkCardTrigger

Hover Card's Trigger: a link, to the Root's href unless it has its own.

PreviewLinkCardPortal, PreviewLinkCardArrow

Hover Card's Portal and Arrow.

PreviewLinkCardContent

Hover Card's Content (transition, exitTransition, side, align…), rendered as a link to the Root's href: it is the card, and its className styles it. With asChild, your element is rendered instead.

PropTypeDefault
side?
'top' | 'right' | 'bottom' | 'left''top'
sideOffset?
number10
align?
'start' | 'center' | 'end''center'
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