Link previews

Shows how a URL will look in a search result (desktop and mobile, truncated by pixel width like the real thing) and as a shared card on Facebook, LinkedIn, X, WhatsApp and Slack, with length meters and image warnings. For SEO panels, product pages and shareable quote links.

Pure (canvas measureText for pixel widths) media/link-preview.tsx, media/serp.ts, styles/media.csstheme · material · accent · density · font

Installation

$ pnpm add @vamos-ui/react

The whole kit as one package — get updates by bumping the version. Add the styles to globals.css: @import "@vamos-ui/react/styles.css";

Usage

import { LinkPreviewTabs, SearchPreview, SocialCardPreview, LengthMeter } from "@vamos-ui/react/media";

<LinkPreviewTabs data={{ url, title, description, siteName, image }} highlight={focusKeyword} />
<LengthMeter value={seoTitle} field="title" label="SEO title" />

The one deliberate token exception: the previews imitate third-party UIs (white background, link blue, system fonts, each platform's own dark theme) through local --preview-* variables in styles/media.css. The frame, meters and warnings use kit tokens.

Preview content is real, readable text (role="figure" with a label); meters never rely on colour alone.

Examples

Search: desktop and mobile

Titles are cut at ≈600 px on desktop with the ellipsis at a word; the focus keyword is bolded.

Desktop

Atlas Lojistikhttps://atlaslojistik.comservicessame-day-delivery-istanbul

Same-Day Delivery in Istanbul for E-commerce Brands …

Order by 13:00, delivered by 21:00 anywhere on both sides of the city. Live tracking, cash on delivery and returns handled by the same …

Mobile

Atlas Lojistikhttps://atlaslojistik.comservicessame-day-delivery-istanbul

Same-Day Delivery in Istanbul for E-commerce Brands, Retailers and Marketplaces | …

Order by 13:00, delivered by 21:00 anywhere on both sides of the city. Live tracking, cash on delivery …

Social cards: every platform

Each platform's own layout and theme. appearance="auto" follows the surrounding light/dark theme.

facebook

KALAMISBISTRO.COMNew Year's Eve at Kalamış Bistro — Seven Courses by the MarinaA seven-course tasting menu, live jazz trio and a midnight toast on the terrace. 64 seats, from ₺6,500 per guest.

linkedin

New Year's Eve at Kalamış Bistro — Seven Courses by the Marinakalamisbistro.com

X

kalamisbistro.comFrom kalamisbistro.com

whatsapp

New Year's Eve at Kalamış Bistro — Seven Courses by the MarinaA seven-course tasting menu, live jazz trio and a midnight toast on the terrace. 64 seats, from ₺6,500 per guest.kalamisbistro.comhttps://kalamisbistro.com/events/new-years-eve-2026

slack

Kalamış BistroNew Year's Eve at Kalamış Bistro — Seven Courses by the MarinaA seven-course tasting menu, live jazz trio and a midnight toast on the terrace. 64 seats, from ₺6,500 per guest.

Missing fields and warnings

Greyed fallbacks, a noindex banner, and warnings for a missing, square or small image — said in words.

Hidden from search engines (noindex)

peradental.comhttps://peradental.comdraftimplants-faq

Untitled page

No description: search engines will pick text from the page.

PERADENTAL.COMDental Implants in Şişli — Pera Dental
  • No social image. The link will show as a plain card.
Meet the Pera Dental teamperadental.com
  • Image is 1:1. LinkedIn shows 1.91:1 and will crop it.
  • Image is 800×800. LinkedIn recommends at least 1200×630.

Length meters (SEO panel)

Counts graphemes and rendered pixels: "52 of 60 characters, 498 of 600 px: good".

Kalamış Bistrohttps://kalamisbistro.comjournalspring-menu-2027

Spring Menu 2027: Twelve New Plates from Our Garden

Asparagus, wild garlic and the first broad beans: our chef walks you through the new spring menu, served from 1 March.

API

LinkPreviewData

PropTypeDefault
url / title
Required. An empty title shows the "Untitled page" fallback.
string—
description
Meta / OG description.
string—
siteName / favicon
Shown in the search header and Slack.
string—
image
OG image. Assets use variants.og when present and are checked for ratio and size.
MediaAsset | string | null—
publishedAt
Date before the description (locale dateStyle: medium).
Date | "YYYY-MM-DD"—
breadcrumbs
Replaces the URL path in the search breadcrumb.
string[]—
twitterCard
X card type.
"summary" | "summary_large_image""summary_large_image"
noindex
Shows "Hidden from search engines".
boolean—

SearchPreview

PropTypeDefault
data
The page.
LinkPreviewData—
device
Layout and truncation widths.
"desktop" | "mobile""desktop"
highlight
Keyword to bold in the description (locale-aware).
string—
appearance
auto follows the surrounding theme.
"light" | "dark" | "auto""auto"
size
sm for sidebars.
"sm" | "md""md"

SocialCardPreview

PropTypeDefault
platform
Which card.
"facebook" | "linkedin" | "x" | "whatsapp" | "slack"—
data / appearance / size
—
as above—
hideWarnings
Hide the image warnings under the card.
booleanfalse

LengthMeter

PropTypeDefault
value
Text to measure (graphemes).
string—
field
Also measure the SERP pixel width and use its ideal range.
"title" | "description"—
max / min
Character limits; below min is "too short".
numberSERP ideal, else 160 / 0
device
Pixel limits.
"desktop" | "mobile""desktop"
label
Accessible name (role="meter").
string—

LinkPreviewTabs

PropTypeDefault
platforms
Tabs to show; search has a desktop/mobile switch.
("search" | SocialPlatform)[]["search", "facebook", "x", "whatsapp"]
defaultValue / highlight / appearance / size
Passed through.
——

measureSerp(text, field, device?, locale?)

PropTypeDefault
→
Pixel width and where a search engine cuts the text. Constants in SERP_LIMITS (versioned).
{ px, maxPx, truncated, cutAt, display }—