Link Preview

Displays a summarized preview of a linked content's details or information.

llms.txt
	<script lang="ts">
  import { Avatar, LinkPreview } from "bits-ui";
  import CalendarBlank from "phosphor-svelte/lib/CalendarBlank";
  import MapPin from "phosphor-svelte/lib/MapPin";
</script>
 
<LinkPreview.Root>
  <LinkPreview.Trigger
    href="https://x.com/huntabyte"
    target="_blank"
    rel="noreferrer noopener"
    class="rounded-xs underline-offset-4 hover:underline focus-visible:outline-2 focus-visible:outline-offset-8 focus-visible:outline-black"
  >
    <Avatar.Root
      class="data-[status=loaded]:border-foreground bg-muted text-muted-foreground h-12 w-12 rounded-full border border-transparent text-[17px] font-medium uppercase"
    >
      <div
        class="flex h-full w-full items-center justify-center overflow-hidden rounded-full border-2 border-transparent"
      >
        <Avatar.Image src="/avatar-1.png" alt="@huntabyte" />
        <Avatar.Fallback class="border-muted border">HB</Avatar.Fallback>
      </div>
    </Avatar.Root>
  </LinkPreview.Trigger>
  <LinkPreview.Content
    class="border-muted bg-background shadow-popover w-[331px] rounded-xl border p-[17px]"
    sideOffset={8}
  >
    <div class="flex space-x-4">
      <Avatar.Root
        class="data-[status=loaded]:border-foreground bg-muted text-muted-foreground h-12 w-12 rounded-full border border-transparent text-[17px] font-medium uppercase"
      >
        <div
          class="flex h-full w-full items-center justify-center overflow-hidden rounded-full border-2 border-transparent"
        >
          <Avatar.Image src="/avatar-1.png" alt="@huntabyte" />
          <Avatar.Fallback class="border-muted border">HB</Avatar.Fallback>
        </div>
      </Avatar.Root>
      <div class="space-y-1 text-sm">
        <h4 class="font-medium">@huntabyte</h4>
        <p>I do things on the internet.</p>
        <div
          class="text-muted-foreground flex items-center gap-[21px] pt-2 text-xs"
        >
          <div class="flex items-center text-xs">
            <MapPin class="mr-1 size-4" />
            <span> FL, USA </span>
          </div>
          <div class="flex items-center text-xs">
            <CalendarBlank class="mr-1 size-4" />
            <span> Joined May 2020</span>
          </div>
        </div>
      </div>
    </div>
  </LinkPreview.Content>
</LinkPreview.Root>

Overview

A component that lets users preview a link before they decide to follow it. This is useful for providing non-essential context or additional information about a link without having to navigate away from the current page.

Structure

	<script lang="ts">
  import { LinkPreview } from "bits-ui";
</script>
 
<LinkPreview.Root>
  <LinkPreview.Trigger />
  <LinkPreview.Content />
</LinkPreview.Root>

Managing Open State

This section covers how to manage the open state of the component.

Two-Way Binding

Use bind:open for simple, automatic state synchronization:

	<script lang="ts">
  import { LinkPreview } from "bits-ui";
  let isOpen = $state(false);
</script>
 
<button onclick={() => (isOpen = true)}>Open Link Preview</button>
 
<LinkPreview.Root bind:open={isOpen}>
  <!-- ... -->
</LinkPreview.Root>

Fully Controlled

Use a Function Binding for complete control over the state's reads and writes.

	<script lang="ts">
  import { LinkPreview } from "bits-ui";
  let myOpen = $state(false);
 
  function getOpen() {
    return myOpen;
  }
 
  function setOpen(newOpen: boolean) {
    myOpen = newOpen;
  }
</script>
 
<LinkPreview.Root bind:open={getOpen, setOpen}>
  <!-- ... -->
</LinkPreview.Root>

Opt-out of Floating UI

When you use the LinkPreview.Content component, Bits UI uses Floating UI to position the content relative to the trigger, similar to other popover-like components.

You can opt-out of this behavior by instead using the LinkPreview.ContentStatic component. This component does not use Floating UI and leaves positioning the content entirely up to you.

	<LinkPreview.Root>
  <LinkPreview.Trigger />
  <LinkPreview.ContentStatic>
    <!-- ... -->
  </LinkPreview.ContentStatic>
</LinkPreview.Root>

Custom Anchor

By default, the LinkPreview.Content is anchored to the LinkPreview.Trigger component, which determines where the content is positioned.

If you wish to instead anchor the content to a different element, you can pass either a selector string or an HTMLElement to the customAnchor prop of the LinkPreview.Content component.

	<script lang="ts">
  import { LinkPreview } from "bits-ui";
  let customAnchor = $state<HTMLElement>(null!);
</script>
 
<div bind:this={customAnchor}></div>
 
<LinkPreview.Root>
  <LinkPreview.Trigger />
  <LinkPreview.Content {customAnchor}>
    <!-- ... -->
  </LinkPreview.Content>
</LinkPreview.Root>

Svelte Transitions

You can use the forceMount prop along with the child snippet to forcefully mount the LinkPreview.Content component to use Svelte Transitions or another animation library that requires more control.

	<script lang="ts">
  import { LinkPreview } from "bits-ui";
  import { fly } from "svelte/transition";
</script>
 
<LinkPreview.Content forceMount>
  {#snippet child({ wrapperProps, props, open })}
    {#if open}
      <div {...wrapperProps}>
        <div {...props} transition:fly>
          <!-- ... -->
        </div>
      </div>
    {/if}
  {/snippet}
</LinkPreview.Content>

Of course, this isn't the prettiest syntax, so it's recommended to create your own reusable content component that handles this logic if you intend to use this approach. For more information on using transitions with Bits UI components, see the Transitions documentation.

	<script lang="ts">
  import { Avatar, LinkPreview } from "bits-ui";
  import CalendarBlank from "phosphor-svelte/lib/CalendarBlank";
  import MapPin from "phosphor-svelte/lib/MapPin";
  import { fly } from "svelte/transition";
 
  let loadingStatusTrigger: Avatar.RootProps["loadingStatus"] =
    $state("loading");
  let loadingStatusContent: Avatar.RootProps["loadingStatus"] =
    $state("loading");
</script>
 
<LinkPreview.Root>
  <LinkPreview.Trigger
    href="https://github.com/sveltejs"
    target="_blank"
    rel="noreferrer noopener"
    class="rounded-xs underline-offset-4 hover:underline focus-visible:outline-2 focus-visible:outline-offset-8 focus-visible:outline-black"
  >
    <Avatar.Root
      bind:loadingStatus={loadingStatusTrigger}
      class="h-12 w-12 rounded-full border {loadingStatusTrigger === 'loaded'
        ? 'border-foreground'
        : 'border-transparent'} bg-muted text-muted-foreground text-[17px] font-medium uppercase"
    >
      <div
        class="flex h-full w-full items-center justify-center overflow-hidden rounded-full border-2 border-transparent"
      >
        <Avatar.Image src="/avatar-1.png" alt="@huntabyte" />
        <Avatar.Fallback class="border-muted border">HB</Avatar.Fallback>
      </div>
    </Avatar.Root>
  </LinkPreview.Trigger>
  <LinkPreview.Content
    class="border-muted bg-background shadow-popover w-[331px] rounded-xl border p-[17px]"
    sideOffset={8}
    forceMount
  >
    {#snippet child({ open, props, wrapperProps })}
      {#if open}
        <div {...wrapperProps}>
          <div {...props} transition:fly={{ duration: 300 }}>
            <div class="flex space-x-4">
              <Avatar.Root
                bind:loadingStatus={loadingStatusContent}
                class="h-12 w-12 rounded-full border {loadingStatusContent ===
                'loaded'
                  ? 'border-foreground'
                  : 'border-transparent'} bg-muted text-muted-foreground text-[17px] font-medium uppercase"
              >
                <div
                  class="flex h-full w-full items-center justify-center overflow-hidden rounded-full border-2 border-transparent"
                >
                  <Avatar.Image src="/avatar-1.png" alt="@huntabyte" />
                  <Avatar.Fallback class="border-muted border"
                    >HB</Avatar.Fallback
                  >
                </div>
              </Avatar.Root>
              <div class="space-y-1 text-sm">
                <h4 class="font-medium">@huntabyte</h4>
                <p>I do things on the internet.</p>
                <div
                  class="text-muted-foreground flex items-center gap-[21px] pt-2 text-xs"
                >
                  <div class="flex items-center text-xs">
                    <MapPin class="mr-1 size-4" />
                    <span> FL, USA </span>
                  </div>
                  <div class="flex items-center text-xs">
                    <CalendarBlank class="mr-1 size-4" />
                    <span> Joined May 2020</span>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      {/if}
    {/snippet}
  </LinkPreview.Content>
</LinkPreview.Root>

API Reference

LinkPreview.Root

The root component used to manage the state of the state of the link preview.

Property Details
open
onOpenChange
onOpenChangeComplete
openDelay
closeDelay
disabled
ignoreNonKeyboardFocus
children
Data Attribute Details

LinkPreview.Trigger

A component which triggers the opening and closing of the link preview on hover or focus.

Property Details
ref
children
child
Data Attribute Details
data-state
data-link-preview-trigger

LinkPreview.Content

The contents of the link preview which are displayed when the preview is open.

Property Details
side
sideOffset
align
alignOffset
arrowPadding
avoidCollisions
collisionBoundary
collisionPadding
sticky
hideWhenDetached
updatePositionStrategy
strategy
preventScroll
customAnchor
onInteractOutside
onFocusOutside
interactOutsideBehavior
onEscapeKeydown
escapeKeydownBehavior
onOpenAutoFocus
onCloseAutoFocus
trapFocus
dir
forceMount
ref
children
child
Data Attribute Details
data-state
data-link-preview-content
CSS Variable Details
--bits-link-preview-content-transform-origin
--bits-link-preview-content-available-width
--bits-link-preview-content-available-height
--bits-link-preview-anchor-width
--bits-link-preview-anchor-height

LinkPreview.ContentStatic

The contents of the link preview which are displayed when the preview is open. (Static/No Floating UI)

Property Details
onInteractOutside
onFocusOutside
interactOutsideBehavior
onEscapeKeydown
escapeKeydownBehavior
onOpenAutoFocus
onCloseAutoFocus
trapFocus
dir
forceMount
ref
children
child
Data Attribute Details
data-state
data-link-preview-content

LinkPreview.Arrow

An optional arrow element which points to the trigger when the preview is open.

Property Details
width
height
ref
children
child
Data Attribute Details
data-link-preview-arrow

LinkPreview.Portal

When used, will render the link preview content into the body or custom to element when open

Property Details
to
disabled
children
Data Attribute Details