Data Display

Hover Card

A popover card that appears on hover to preview additional content without navigating away.

Usage

HoverCard reveals a floating panel when the pointer rests on a trigger, letting you preview rich content — a profile, a thumbnail, a summary — without a click or a navigation. Hovering opens it after openDelay; leaving the trigger and the card closes it after closeDelay, so the pointer can travel from one to the other without it vanishing.

Focus opens the card immediately rather than on a timer, and Escape closes it while it is open. The trigger is a wrapper, not a control: give it a genuinely focusable child — a link or a button — or there is nothing for the keyboard to reach.

Anatomy

Import the HoverCard component and access all parts using dot notation.

import {HoverCard} from "@blakeui/pro-react";

<HoverCard>
  <HoverCard.Trigger />
  <HoverCard.Content>
    <HoverCard.Arrow />
  </HoverCard.Content>
</HoverCard>;

HoverCard.Arrow is optional; everything else is required. HoverCard.Content wraps a React Aria Popover and renders into a portal, so it escapes any overflow clipping between it and the trigger.

Controlled

@blakeui

Pass open with onOpenChange to drive the card yourself, or defaultOpen to set the initial state and leave the component to manage it.

Custom Delays

openDelay defaults to 700ms and closeDelay to 300ms. Either at 0 — or below — skips the timer and switches immediately.

Placements

placement sits on HoverCard.Content and defaults to 'top'. It is a preference rather than a guarantee: React Aria flips the card to the opposite side when the chosen one would run past the viewport, and data-placement always reports where it actually landed.

With Arrow

With Image

CSS Classes

Element Classes

  • .hover-card__trigger — The inline wrapper around your trigger element. display: inline-flex, so it takes no layout space of its own.
  • .hover-card__content — The floating panel. max-width: var(--container-sm, 24rem), padding: calc(var(--spacing) * 4), border-radius: calc(var(--radius) * 2), background-color: var(--overlay) and outline-style: none. It keeps box-shadow: var(--overlay-shadow): this is a genuinely floating surface, so the elevation is doing real work rather than decorating a card.
  • .hover-card__arrow — The optional pointer connecting content to trigger. display: block, with its svg filled var(--overlay) so the arrow and the panel read as one shape.

Interactive States

Both state attributes are set by React Aria on .hover-card__content, and both carry the literal value "true" rather than being bare.

  • [data-entering="true"]animation: enter 0.15s var(--ease-out, cubic-bezier(0, 0, 0.2, 1)), from --tw-enter-opacity: 0 and --tw-enter-scale: 0.97.
  • [data-exiting="true"]animation: exit 0.1s var(--ease-out, …), to --tw-exit-opacity: 0 and --tw-exit-scale: 0.97. The card stays mounted until this animation ends.
  • Both add will-change: opacity, transform for the duration of the animation, and no longer.
  • [data-placement="top|bottom|left|right"] — On the content, picks the direction the entrance slides from: one --spacing unit along the axis it opened on, so the card always arrives out of its trigger. On the arrow, rotates the svg180deg at bottom, -90deg at left, 90deg at right.

Motion

Reduced motion is gentler, never zero: the fade survives at full length and only the movement — the 0.97 scale and the directional slide — is neutralised, so the card still reads as arriving rather than teleporting. The opt-out is dual and both halves ship: an explicit [data-reduce-motion="true"] on the card or any ancestor, and @media (prefers-reduced-motion: reduce) scoped so the two never double-apply.

API Reference

HoverCard

The root. Owns the open state and the open/close timers, and hands them to the parts through context. Renders no DOM of its own.

PropTypeDefaultDescription
childrenReactNodeA HoverCard.Trigger and a HoverCard.Content. Required.
openbooleanControlled open state. Pair it with onOpenChange.
defaultOpenbooleanfalseInitial open state when uncontrolled.
onOpenChange(open: boolean) => voidCalled whenever the card wants to open or close — hover, focus, Escape, or an interaction outside it.
openDelaynumber700Milliseconds the pointer must rest on the trigger before the card opens. 0 or less opens immediately. Focus ignores this and opens at once.
closeDelaynumber300Milliseconds before the card closes once the pointer has left both the trigger and the card. This is the grace period that lets the pointer cross the gap between them.

HoverCard.Trigger

The hoverable element — a <span> that tracks pointer and focus events on whatever you put inside it. Accepts every native <span> attribute, including ref; its own handlers compose with yours rather than replacing them.

PropTypeDefaultDescription
childrenReactNodeThe element that opens the card. Make it focusable — a link or a button — so the card is reachable by keyboard. Required.
classNamestringClass name for the trigger wrapper.

HoverCard.Content

The floating panel. Wraps React Aria Popover and accepts all of its props except isOpen and triggerRef, which the root supplies.

PropTypeDefaultDescription
childrenReactNode | (renderProps) => ReactNodeThe card's content, and optionally a HoverCard.Arrow. A function receives React Aria's popover render props. Required.
placementPlacement"top"Preferred side, flipped automatically when it would overflow the viewport. Read the resolved side off data-placement.
offsetnumber8Distance in pixels between the card and its trigger.
classNamestring | (renderProps) => stringClass name for the panel.

HoverCard.Arrow

The optional pointer. Wraps React Aria OverlayArrow and accepts all of its props.

PropTypeDefaultDescription
childrenReactNodea 12×12 SVGA custom arrow. Left empty, the component draws its own triangle and rotates it to match data-placement.
classNamestring | (renderProps) => stringClass name for the arrow.

On this page