Hover Card

For sighted users to preview content available behind a link.

Client JS
@nextjs
The React Framework – created and maintained by @vercel.
Joined December 2021
import { Button } from "@/components/ui/button"
import {
  HoverCard,
  HoverCardContent,
  HoverCardTrigger,
} from "@/components/ui/hover-card"

export default function HoverCardDemo() {
  return (
    <HoverCard>
      <HoverCardTrigger
        delay={10}
        closeDelay={100}
        render={<Button variant="link" />}
      >
        Hover Here
      </HoverCardTrigger>
      <HoverCardContent class="flex w-64 flex-col gap-0.5">
        <div class="font-semibold">@nextjs</div>
        <div>The React Framework – created and maintained by @vercel.</div>
        <div class="mt-1 text-xs text-muted-foreground">
          Joined December 2021
        </div>
      </HoverCardContent>
    </HoverCard>
  )
}

Installation

pnpm dlx shadcnui-hono-jsx@latest add hover-card

This component uses a small client script. add installs it into public/shadcn/; serve that directory at /shadcn/ and load the script on pages that use the component (see Client Scripts):

<script type="module" src="/shadcn/hover.js"></script>

Usage

import {
  HoverCard,
  HoverCardContent,
  HoverCardTrigger,
} from "@/components/ui/hover-card"
<HoverCard>
  <HoverCardTrigger>Hover</HoverCardTrigger>
  <HoverCardContent>
    The React Framework – created and maintained by @vercel.
  </HoverCardContent>
</HoverCard>

Composition

Use the following composition to build a HoverCard:

HoverCard
├── HoverCardTrigger
└── HoverCardContent

Trigger Delays

Use delay and closeDelay on the trigger to control when the card opens and closes.

<HoverCard>
  <HoverCardTrigger delay={100} closeDelay={200}>
    Hover
  </HoverCardTrigger>
  <HoverCardContent>Content</HoverCardContent>
</HoverCard>

Positioning

Use the side and align props on HoverCardContent to control placement.

<HoverCard>
  <HoverCardTrigger>Hover</HoverCardTrigger>
  <HoverCardContent side="top" align="start">
    Content
  </HoverCardContent>
</HoverCard>

Basic

@nextjs
The React Framework – created and maintained by @vercel.
Joined December 2021
import { Button } from "@/components/ui/button"
import {
  HoverCard,
  HoverCardContent,
  HoverCardTrigger,
} from "@/components/ui/hover-card"

export default function HoverCardDemo() {
  return (
    <HoverCard>
      <HoverCardTrigger
        delay={10}
        closeDelay={100}
        render={<Button variant="link" />}
      >
        Hover Here
      </HoverCardTrigger>
      <HoverCardContent class="flex w-64 flex-col gap-0.5">
        <div class="font-semibold">@nextjs</div>
        <div>The React Framework – created and maintained by @vercel.</div>
        <div class="mt-1 text-xs text-muted-foreground">
          Joined December 2021
        </div>
      </HoverCardContent>
    </HoverCard>
  )
}

Sides

Hover Card

This hover card appears on the left side of the trigger.

Hover Card

This hover card appears on the top side of the trigger.

Hover Card

This hover card appears on the bottom side of the trigger.

Hover Card

This hover card appears on the right side of the trigger.

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

const HOVER_CARD_SIDES = ["left", "top", "bottom", "right"] as const

export function HoverCardSides() {
  return (
    <div class="flex flex-wrap justify-center gap-2">
      {HOVER_CARD_SIDES.map((side) => (
        <HoverCard key={side}>
          <HoverCardTrigger
            delay={100}
            closeDelay={100}
            render={<Button variant="outline" class="capitalize" />}
          >
            {side}
          </HoverCardTrigger>
          <HoverCardContent side={side}>
            <div class="flex flex-col gap-1">
              <h4 class="font-medium">Hover Card</h4>
              <p>This hover card appears on the {side} side of the trigger.</p>
            </div>
          </HoverCardContent>
        </HoverCard>
      ))}
    </div>
  )
}

RTL

To enable RTL support in shadcn/ui, see the RTL configuration guide.

سماعات لاسلكية
٩٩.٩٩ $
سماعات لاسلكية
٩٩.٩٩ $
سماعات لاسلكية
٩٩.٩٩ $
سماعات لاسلكية
٩٩.٩٩ $
سماعات لاسلكية
٩٩.٩٩ $
سماعات لاسلكية
٩٩.٩٩ $

// This example sets dir and uses useTranslation because this site is not RTL.

// In an RTL app you won't need them; see the RTL guide.

import {
  type Translations,
  useTranslation,
} from "@/components/language-selector"
import { Button } from "@/components/ui/button"
import {
  HoverCard,
  HoverCardContent,
  HoverCardTrigger,
} from "@/components/ui/hover-card"

const translations: Translations = {
  en: {
    dir: "ltr",
    values: {
      trigger: "Wireless Headphones",
      name: "Wireless Headphones",
      price: "$99.99",
      "inline-start": "Inline Start",
      left: "Left",
      top: "Top",
      bottom: "Bottom",
      right: "Right",
      "inline-end": "Inline End",
    },
  },
  ar: {
    dir: "rtl",
    values: {
      trigger: "سماعات لاسلكية",
      name: "سماعات لاسلكية",
      price: "٩٩.٩٩ $",
      "inline-start": "بداية السطر",
      left: "يسار",
      top: "أعلى",
      bottom: "أسفل",
      right: "يمين",
      "inline-end": "نهاية السطر",
    },
  },
  he: {
    dir: "rtl",
    values: {
      trigger: "אוזניות אלחוטיות",
      name: "אוזניות אלחוטיות",
      price: "99.99 $",
      "inline-start": "תחילת השורה",
      left: "שמאל",
      top: "למעלה",
      bottom: "למטה",
      right: "ימין",
      "inline-end": "סוף השורה",
    },
  },
}

const physicalSides = ["left", "top", "bottom", "right"] as const
const logicalSides = ["inline-start", "inline-end"] as const

export function HoverCardRtl() {
  const { dir, t } = useTranslation(translations, "ar")

  return (
    <div class="grid gap-4">
      <div class="flex flex-wrap justify-center gap-2">
        {physicalSides.map((side) => (
          <HoverCard key={side}>
            <HoverCardTrigger
              delay={10}
              closeDelay={100}
              render={<Button variant="outline" />}
            >
              {t[side]}
            </HoverCardTrigger>
            <HoverCardContent
              side={side}
              class="flex w-64 flex-col gap-1"
              dir={dir}
            >
              <div class="font-semibold">{t.name}</div>
              <div class="text-sm text-muted-foreground">{t.price}</div>
            </HoverCardContent>
          </HoverCard>
        ))}
      </div>
      <div class="flex flex-wrap justify-center gap-2">
        {logicalSides.map((side) => (
          <HoverCard key={side}>
            <HoverCardTrigger
              delay={10}
              closeDelay={100}
              render={<Button variant="outline" />}
            >
              {t[side]}
            </HoverCardTrigger>
            <HoverCardContent
              side={side}
              class="flex w-64 flex-col gap-1"
              dir={dir}
            >
              <div class="font-semibold">{t.name}</div>
              <div class="text-sm text-muted-foreground">{t.price}</div>
            </HoverCardContent>
          </HoverCard>
        ))}
      </div>
    </div>
  )
}

Notes

  • Opening on hover or keyboard focus needs the client script /shadcn/hover.js (<script type="module" src="/shadcn/hover.js">); without it the card does not open and the trigger works as a plain link. The card is a native popover placed with CSS anchor positioning.
  • Controlled state (open, onOpenChange) is not supported.
  • Accepts class instead of className.
  • Upstream documentation: shadcn/ui Hover Card. See also Differences from shadcn/ui.

API Reference

See the Base UI documentation for more information.