Skip to content
Corsair UI
Browse the docs

ComponentsNo. 29 of 109

Hover Card

A preview card that appears while a link is hovered or focused, for supplementary content.

Installation

pnpm dlx shadcn@latest add @corsair-ui/hover-card

No setup needed: the CLI finds @corsair-ui in the shadcn registry directory. The files land in your project as source: edit them like your own code. More in Installation.

Also adds
utils
npm
@radix-ui/react-hover-cardclsxtailwind-merge

Examples

Profile preview

Hover or focus the link. The link still works on its own.

Quartermaster: @annebonny keeps the peace aboard.

API

PropTypeDefault
openDelay / closeDelay

HoverCard: in ms, long enough to move onto the card.

number400 / 150
align / sideOffset

HoverCardContent: position next to the trigger.

"start" | "center" | "end" / number"center" / 4

Built on Radix Hover Card; every prop it takes is passed through.

Accessibility

  • Opens on hover and on keyboard focus of the trigger.
  • Touch and screen reader users may never see it: the link must work on its own and the card holds nothing essential.
  • It appears without fading or zooming with prefers-reduced-motion.

Compatibility

Tailwind CSS
3.4 and 4, both checked in CI
React
19
Rendering
Client component ("use client")

Source

1 file, installed as source you own
hover-card.tsxView on GitHub
"use client";

import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
import type { ComponentProps } from "react";

import { cn } from "@/registry/default/lib/utils";

/**
 * A preview card that appears while the pointer rests on a link, or while
 * the link has keyboard focus, and goes away when either leaves. Use it for
 * supplementary content only: touch and screen reader users may never see
 * the card, so the trigger must lead somewhere on its own (a profile page,
 * the full article) and nothing in the card may be the only way to do
 * something. The card opens after `openDelay` (400ms) and closes after
 * `closeDelay` (150ms), long enough to move the pointer onto it. With
 * reduced motion it appears without fading or zooming.
 *
 * @example
 * <HoverCard>
 *   <HoverCardTrigger asChild>
 *     <a href="/users/ada">@ada</a>
 *   </HoverCardTrigger>
 *   <HoverCardContent>
 *     <p className="font-medium">Ada Lovelace</p>
 *     <p className="text-muted-foreground">Writes about analytical engines.</p>
 *   </HoverCardContent>
 * </HoverCard>
 */
function HoverCard({
  openDelay = 400,
  closeDelay = 150,
  ...props
}: ComponentProps<typeof HoverCardPrimitive.Root>) {
  return (
    <HoverCardPrimitive.Root
      data-slot="hover-card"
      openDelay={openDelay}
      closeDelay={closeDelay}
      {...props}
    />
  );
}

/** The link the card belongs to. It renders an `<a>`; use `asChild` for your router's link. */
function HoverCardTrigger(props: ComponentProps<typeof HoverCardPrimitive.Trigger>) {
  return <HoverCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} />;
}

/** The card, rendered in a portal next to the trigger. */
function HoverCardContent({
  className,
  align = "center",
  sideOffset = 4,
  ...props
}: ComponentProps<typeof HoverCardPrimitive.Content>) {
  return (
    <HoverCardPrimitive.Portal>
      <HoverCardPrimitive.Content
        data-slot="hover-card-content"
        align={align}
        sideOffset={sideOffset}
        className={cn(
          "bg-popover text-popover-foreground z-50 w-64 origin-[var(--radix-hover-card-content-transform-origin)] rounded-md border p-4 shadow-md outline-none",
          // animate-in/out sit behind motion-safe: so reduced motion really turns them off.
          "motion-safe:data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
          "motion-safe:data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
          "data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
          className
        )}
        {...props}
      />
    </HoverCardPrimitive.Portal>
  );
}

export { HoverCard, HoverCardContent, HoverCardTrigger };