Free shadcn/ui hover card for React — a rich preview that opens on hover, built on Radix: a profile card, a user mention, and an inline glossary term.

Installation

1 Configure registry — once per project
{
  "registries": {
    "@designrevision": "https://registry.designrevision.com/r/{name}.json"
  }
}

Add to your existing components.json. Requires shadcn/ui v2.3+.

2 Install the component

Packages

@radix-ui/react-hover-card
utils

Props

No props documented yet.

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

import * as React from "react"
import * as HoverCardPrimitive from "@radix-ui/react-hover-card"

import { cn } from "@/lib/utils"

function HoverCard({
  ...props
}: React.ComponentProps<typeof HoverCardPrimitive.Root>) {
  return <HoverCardPrimitive.Root data-slot="hover-card" {...props} />
}

function HoverCardTrigger({
  ...props
}: React.ComponentProps<typeof HoverCardPrimitive.Trigger>) {
  return (
    <HoverCardPrimitive.Trigger data-slot="hover-card-trigger" {...props} />
  )
}

function HoverCardContent({
  className,
  align = "center",
  sideOffset = 4,
  ...props
}: React.ComponentProps<typeof HoverCardPrimitive.Content>) {
  return (
    <HoverCardPrimitive.Portal data-slot="hover-card-portal">
      <HoverCardPrimitive.Content
        data-slot="hover-card-content"
        align={align}
        sideOffset={sideOffset}
        className={cn(
          "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-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 z-50 w-64 origin-(--radix-hover-card-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
          className
        )}
        {...props}
      />
    </HoverCardPrimitive.Portal>
  )
}

export { HoverCard, HoverCardTrigger, HoverCardContent }

Examples

Basic

The canonical profile-preview hover card.

Packages

lucide-react
hover-card
avatar
button

Props

No props documented yet.

Copied!
components/ui/hover-card-demo-01.tsx
import { CalendarDays } from "lucide-react"

import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import {
  HoverCard,
  HoverCardContent,
  HoverCardTrigger,
} from "@/components/ui/hover-card"

export default function HoverCardDemo01() {
  return (
    <HoverCard>
      <HoverCardTrigger asChild>
        <Button variant="link">@nextjs</Button>
      </HoverCardTrigger>
      <HoverCardContent className="w-80">
        <div className="flex justify-between gap-4">
          <Avatar>
            <AvatarImage src="https://github.com/vercel.png" />
            <AvatarFallback>VC</AvatarFallback>
          </Avatar>
          <div className="space-y-1">
            <h4 className="text-sm font-semibold">@nextjs</h4>
            <p className="text-sm">
              The React Framework – created and maintained by @vercel.
            </p>
            <div className="text-muted-foreground flex items-center pt-2 text-xs">
              <CalendarDays className="mr-2 size-4" />
              Joined December 2021
            </div>
          </div>
        </div>
      </HoverCardContent>
    </HoverCard>
  )
}

User mention

An @mention preview with follower stats and a follow button.

Packages

hover-card
avatar
button

Props

No props documented yet.

Copied!
components/ui/hover-card-user-01.tsx
import { Avatar, AvatarFallback } from "@/components/ui/avatar"
import { Button } from "@/components/ui/button"
import {
  HoverCard,
  HoverCardContent,
  HoverCardTrigger,
} from "@/components/ui/hover-card"

export default function HoverCardUser01() {
  return (
    <HoverCard>
      <HoverCardTrigger asChild>
        <a href="#" className="font-medium underline-offset-4 hover:underline">
          @ada
        </a>
      </HoverCardTrigger>
      <HoverCardContent className="w-72">
        <div className="flex gap-3">
          <Avatar>
            <AvatarFallback>AL</AvatarFallback>
          </Avatar>
          <div className="space-y-1.5">
            <div>
              <p className="text-sm font-semibold">Ada Lovelace</p>
              <p className="text-muted-foreground text-xs">
                @ada · Mathematician
              </p>
            </div>
            <div className="flex gap-3 text-xs">
              <span>
                <span className="text-foreground font-medium">1,204</span>{" "}
                <span className="text-muted-foreground">Followers</span>
              </span>
              <span>
                <span className="text-foreground font-medium">180</span>{" "}
                <span className="text-muted-foreground">Following</span>
              </span>
            </div>
            <Button size="sm" className="mt-1 h-7 w-full">
              Follow
            </Button>
          </div>
        </div>
      </HoverCardContent>
    </HoverCard>
  )
}

Glossary term

An inline term that reveals a definition on hover, with a small open delay.

Packages

hover-card

Props

No props documented yet.

Copied!
components/ui/hover-card-link-01.tsx
import {
  HoverCard,
  HoverCardContent,
  HoverCardTrigger,
} from "@/components/ui/hover-card"

export default function HoverCardLink01() {
  return (
    <p className="max-w-sm text-sm leading-relaxed">
      Install components from the{" "}
      <HoverCard openDelay={120}>
        <HoverCardTrigger asChild>
          <span className="cursor-help font-medium underline decoration-dotted underline-offset-4">
            CLI
          </span>
        </HoverCardTrigger>
        <HoverCardContent className="w-72">
          <p className="text-sm font-medium">Command-line interface</p>
          <p className="text-muted-foreground mt-1 text-sm">
            A text-based way to run programs by typing commands — here, to add
            components to your project.
          </p>
        </HoverCardContent>
      </HoverCard>{" "}
      and they're copied straight into your codebase.
    </p>
  )
}

Frequently asked questions

It is a rich preview that opens when you hover (or focus) a trigger, built on @radix-ui/react-hover-card. Unlike a tooltip, it can hold full content — avatars, stats, buttons, links — and it stays open while you move into it. It is the pattern behind a profile preview on an @mention or a definition on a linked term.
A Tooltip shows a short text label and is meant for naming a control. A Hover Card shows rich, interactive content — images, headings, links, a button — and you can move the pointer into it. Use a tooltip for a one-line hint; use a hover card for a preview the user might read or click inside.
Pass openDelay and closeDelay (in milliseconds) on the HoverCard root: . A short open delay stops the card from flashing as the pointer crosses the trigger; a small close delay gives the user time to move into the card without it snapping shut.
No — hover does not exist on touch, so a hover card never opens by tap. Treat it as a progressive enhancement for pointer users and never hide essential information inside it. On touch, the underlying link or mention should still work on its own, and a Popover (which opens on click) is the right choice when the content must be reachable everywhere.
Wrap your element with HoverCardTrigger and pass asChild so it keeps its own tag: @nextjs. asChild merges the trigger behaviour onto your link or span instead of rendering an extra element, so inline mentions and glossary terms stay inline.
It opens on keyboard focus as well as hover, so keyboard users reach it by tabbing to the trigger, and Radix manages the ARIA wiring. Because it is hover/focus only and absent on touch, keep its content non-essential — supplementary context the user can read, not the only place an action lives.