Skip to content
Highlight

V2 componentsUpdated Oct 14, 2026

Preview Card

A hover card for inline links. The trigger is an anchor inside running text; the card opens after a short delay.

Source: packages/ui/src/components/preview-card

tsx
import { PreviewCard } from '@highlight/ui'

Change the props; the preview and the snippet update together.

Follow-up posted in #design-crit.

Props
400
8
Icon
Show backdrop
tsx
<PreviewCard>  <PreviewCard.Trigger href="#" icon={<MessageSquareText />}>#design-crit</PreviewCard.Trigger>  <PreviewCard.Content className="w-72">    <p className="text-sm font-medium">Weekly critique thread.</p>  </PreviewCard.Content></PreviewCard>

In the launch retro, the team agreed to move the release date and split the migration into two steps.

tsx
import { PreviewCard } from '@highlight/ui' export default function PreviewCardBasic() {  return (    <PreviewCard>      <p className="text-foreground-secondary max-w-sm text-sm">        In the <PreviewCard.Trigger href="#">launch retro</PreviewCard.Trigger>, the team agreed to move the release        date and split the migration into two steps.      </p>      <PreviewCard.Content className="w-72">        <p className="text-foreground text-sm font-medium">Launch retro</p>        <p className="text-foreground-secondary mt-1 text-xs">Tue, Sep 2 · 45 min · 6 attendees</p>        <p className="text-foreground-secondary mt-2 text-sm">Three decisions, two open questions.</p>      </PreviewCard.Content>    </PreviewCard>  )}

icon renders inline before the link text.

Follow-up posted in #design-crit.

tsx
import { MessageSquareText } from 'lucide-react'import { PreviewCard } from '@highlight/ui' export default function PreviewCardIcon() {  return (    <PreviewCard>      <p className="text-foreground-secondary max-w-sm text-sm">        Follow-up posted in{' '}        <PreviewCard.Trigger href="#" icon={<MessageSquareText />}>          #design-crit        </PreviewCard.Trigger>        .      </p>      <PreviewCard.Content className="w-72">        <p className="text-foreground text-sm font-medium">#design-crit</p>        <p className="text-foreground-secondary mt-1 text-sm">Weekly critique thread. 14 replies.</p>      </PreviewCard.Content>    </PreviewCard>  )}

unstyled drops the inline-link look so the trigger can be a pill or a card; render picks its element. The card is Popover’s surface: overlay ground, panel radius, shadow edge, lands at once and fades out.

tsx
import { FileText } from 'lucide-react'import { PreviewCard } from '@highlight/ui' export default function PreviewCardAttachment() {  return (    <PreviewCard>      <PreviewCard.Trigger        unstyled        delay={300}        closeDelay={200}        render={<button type="button" />}        className="bg-element hover:bg-element-hover text-foreground inline-flex h-7 items-center gap-1.5 rounded-full px-2.5 text-sm"      >        <FileText className="size-3.5" aria-hidden />        Q3 planning.pdf      </PreviewCard.Trigger>      <PreviewCard.Content        showBackdrop={false}        className="w-64"        positionerProps={{ side: 'top', align: 'start', sideOffset: 8 }}      >        <div className="bg-element aspect-[4/3] rounded-[calc(var(--radius-lg)-12px)]" aria-hidden />        <div>          <p className="text-foreground text-sm font-medium">Q3 planning.pdf</p>          <p className="text-foreground-secondary text-xs">12 pages · 2.4 MB</p>        </div>      </PreviewCard.Content>    </PreviewCard>  )}

The root, then any compound sub-components. Every part accepts className and forwards the rest of its props to the element or Base UI part beneath it.

PreviewCard
Base UI PreviewCard.Root. Owns open state; opens on hover of its trigger after a delay.
PreviewCard.Trigger
Anchor; href, icon, delay (default 400ms), closeDelay. unstyled drops the link styling for a pill or card trigger.
PreviewCard.Content
Portal + Backdrop + Positioner + Popup. sideOffset, showBackdrop, positionerProps.