Skip to contentVibraUI
Utilities

Hover card

A preview that opens while a link is hovered or focused.

Vibra draws the card as the kit's elev-1 floating layer and fades it in over --duration-base without shadcn's zoom, sliding 4px from its side rather than 8. It is Base UI's PreviewCard, so it opens on keyboard focus as well as on hover, and its trigger is a link that goes where the card previews; keep anything essential out of it — the card is a shortcut, and nothing in it can be reached from the keyboard.

Install

npx shadcn@latest add @vibra/hover-card

Needs the @vibra registry in your components.json — set it up once.

Examples

Deal owner: Saoirse Byrne

A team of faces

Each face is its person's link, named for them; resting on it previews their role and whether it is a sensible hour to message them.

A record

A customer named in an activity feed previews what they pay and how they stand, without leaving the feed.

Harbour Logistics moved to the Scale plan, adding 6 seats. Renewal is due on October 1.

Loads on open

The order is fetched the first time the card opens: busy and said while it comes, in the shape of the answer, and kept for the next visit.

Refund requested on NW-4821, three days after delivery.

Props

PropTypeDefaultDescription
HoverCardTrigger.hrefstring—Where the link goes: the page the card previews.
onOpenChange(open: boolean) => void—Start fetching the preview the first time it opens, and keep the answer.

Dependencies

Source

components/ui/hover-card.tsx
"use client"

import { PreviewCard as PreviewCardPrimitive } from "@base-ui/react/preview-card"
import { cn } from "@/lib/utils"

function HoverCard({ ...props }: PreviewCardPrimitive.Root.Props) {
  return <PreviewCardPrimitive.Root data-slot="hover-card" {...props} />
}

function HoverCardTrigger({ ...props }: PreviewCardPrimitive.Trigger.Props) {
  return (
    <PreviewCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} />
  )
}

function HoverCardContent({
  className,
  side = "bottom",
  sideOffset = 4,
  align = "center",
  alignOffset = 4,
  ...props
}: PreviewCardPrimitive.Popup.Props &
  Pick<
    PreviewCardPrimitive.Positioner.Props,
    "align" | "alignOffset" | "side" | "sideOffset"
  >) {
  return (
    <PreviewCardPrimitive.Portal data-slot="hover-card-portal">
      <PreviewCardPrimitive.Positioner
        align={align}
        alignOffset={alignOffset}
        side={side}
        sideOffset={sideOffset}
        className="isolate z-50"
      >
        <PreviewCardPrimitive.Popup
          data-slot="hover-card-content"
          className={cn(
            "z-50 w-64 origin-(--transform-origin) elev-1 p-2.5 text-sm text-popover-foreground outline-hidden data-[side=bottom]:slide-in-from-top-1 data-[side=inline-end]:slide-in-from-left-1 data-[side=inline-start]:slide-in-from-right-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1 data-open:animate-in data-open:fade-in-0 data-open:duration-(--duration-base) data-open:ease-(--ease-standard) data-closed:animate-out data-closed:fade-out-0 data-closed:duration-(--duration-fast) data-closed:ease-(--ease-exit)",
            className
          )}
          {...props}
        />
      </PreviewCardPrimitive.Positioner>
    </PreviewCardPrimitive.Portal>
  )
}

export { HoverCard, HoverCardTrigger, HoverCardContent }