Link Preview
Hover card preview.
Usage
The hover-card pattern: peek at what is behind a link without following it. Content portals to <body> automatically, so z-index stacking stays clean.
Know the limits
This card opens on pointer hover only. It does not open on focus, so keyboard users never see it, and there is no hover on touch, so mobile users never see it either.
That makes it enrichment, never a delivery mechanism. Everything inside the card must be
reachable another way — put an action or a fact only in here and you have hidden it from a
large share of your users. The upside is that the trigger stays a real <a href>, so the destination itself is always reachable.
If the content is essential, use a Popover — it opens on click, which works for every input method.
The trigger reports as a button
Worth knowing before you ship it: Bits renders the trigger as an anchor with your href, but overrides its role to button with aria-haspopup="dialog". A screen
reader therefore announces "button", not "link", so the element does not advertise that it
navigates.
The practical consequence: do not rely on the trigger alone to communicate a destination. Make the link text say where it goes, or provide the same navigation somewhere that is announced as a link.
Props
LinkPreview.Root
| Prop | Type | Default |
|---|---|---|
children bits-ui | Snippet<[]> | undefined | — |
closeDelay bits-ui The delay in milliseconds before the preview closes. | number | undefined | 300 |
disabled bits-ui When `true`, the preview will be disabled and will not open. | boolean | undefined | false |
ignoreNonKeyboardFocus bits-ui Prevent the preview from opening if the focus did not come using the keyboard. | boolean | undefined | false |
onOpenChange bits-ui A callback that will be called when the link preview is opened or closed. | OnChangeFn<boolean> | undefined | — |
onOpenChangeComplete bits-ui A callback that will be called when the link preview finishes opening/closing animations. | OnChangeFn<boolean> | undefined | — |
open bits-ui The open state of the link preview. | boolean | undefined | false |
openDelay bits-ui The delay in milliseconds before the preview opens. | number | undefined | 700 |
LinkPreview.Trigger
| Prop | Type | Default |
|---|---|---|
child bits-ui | Snippet<[{ props: Record<string, unknown>; }]> | undefined | — |
children bits-ui | Snippet<[]> | undefined | — |
href html | string | null | undefined | — |
id bits-ui | string | undefined | — |
ref bits-ui | HTMLElement | null | undefined | — |
LinkPreview.Content
| Prop | Type | Default |
|---|---|---|
class Extra classes merged onto the floating card. | string | — |
align bits-ui The preferred alignment of the anchor to render against when open. This may change when collisions occur. | "start" | "center" | "end" | undefined | — |
alignOffset bits-ui An offset in pixels from the "start" or "end" alignment options. | number | undefined | — |
arrowPadding bits-ui This describes the padding between the arrow and the edges of the floating element. If your floating element has border-radius, this will prevent it from overflowing the corners. | number | undefined | — |
avoidCollisions bits-ui When `true`, overrides the `side` and `align` options to prevent collisions with the boundary edges. | boolean | undefined | true |
child bits-ui | Snippet<[FloatingContentSnippetProps & { props: Record<string, unknown>; }]> | undefined | — |
children bits-ui | Snippet<[]> | undefined | — |
collisionBoundary bits-ui A boundary element or array of elements to check for collisions against. | Arrayable<Boundary> | undefined | — |
collisionPadding bits-ui The amount in pixels of virtual padding around the viewport edges to check for overflow which will cause a collision. | number | Partial<Record<"top" | "right" | "bottom" | "left", number>> | undefined | 8 |
customAnchor bits-ui Use an element other than the trigger to anchor the content to. If provided, the content will be anchored to the provided element instead of the trigger. You can pass a selector string or an HTMLElement. | string | HTMLElement | Measurable | null | undefined | — |
dir bits-ui The text direction of the content. | Direction | undefined | — |
escapeKeydownBehavior bits-ui Escape behavior type. `close`: Closes the element immediately. `defer-otherwise-close`: Delegates the action to its parent component that has an escape keydown handler. If no parent is found, it closes the element. `defer-otherwise-ignore`: Delegates the action to the parent element. If no parent is found, nothing is done. `ignore`: Prevents the element from closing and also blocks the parent element from closing in response to an escape key press. | EscapeBehaviorType | undefined | `close` |
forceMount bits-ui When `true`, the link preview content will be forced to mount in the DOM. Useful for more control over the transition behavior. | boolean | undefined | — |
hideWhenDetached bits-ui | boolean | undefined | — |
id bits-ui | string | undefined | — |
interactOutsideBehavior bits-ui Interact outside behavior type. `close`: Closes the element immediately. `defer-otherwise-close`: Delegates the action to the parent element. If no parent is found, it closes the element. `defer-otherwise-ignore`: Delegates the action to the parent element. If no parent is found, nothing is done. `ignore`: Prevents the element from closing and also blocks the parent element from closing in response to an outside interaction. | InteractOutsideBehaviorType | undefined | `close` |
onEscapeKeydown bits-ui Callback fired when escape is pressed. | ((e: KeyboardEvent) => void) | undefined | — |
onFocusOutside bits-ui Callback fired when focus leaves the dismissible layer. | ((event: FocusEvent) => void) | undefined | — |
onInteractOutside bits-ui Callback fired when an outside interaction event completes, which is either a `pointerup`, `mouseup`, or `touchend` event, depending on the user's input device. | InteractOutsideEventHandler | undefined | — |
ref bits-ui | HTMLElement | null | undefined | — |
side bits-ui The preferred side of the anchor to render against when open. Will be reversed when collisions occur. | "top" | "right" | "bottom" | "left" | undefined | — |
sideOffset bits-ui The distance in pixels from the anchor to the floating element. | number | undefined | — |
sticky bits-ui | "partial" | "always" | undefined | — |
Root, Trigger and Arrow are re-exported from Bits unchanged, so their props are forwarded
rather than redeclared.
LinkPreview.Trigger — plus the native anchor attributes.
LinkPreview.Content — plus the Bits floating-position props (side, align, sideOffset).