Skip to main content
LinkPreviewField ← Back to Table of Contents

Summary

URL input with a live Open Graph / HTML meta preview card. The field uses the FlexTextInput pill shell for the input track and fetches page metadata through a server-side scrape endpoint (cached, rate-limitable). Preview cards support three layouts, optional URL prefix/suffix affixes, and configurable debounce / skeleton timing.

Basic usage

On a Filament resource:

State format

Important: when prefix('https://') is set, the stored state is always the full URL (https://example.com/path). The visible input shows only the suffix (example.com/path) for readability. On blur, pasted full URLs are normalized back to suffix-only display.
Whitespace is trimmed on hydrate and dehydrate. Empty strings become null.

Preview layouts

Three card layouts via previewLayout():
The preview card is hidden when metadata is empty (no title, description, or image). A skeleton shimmer shows while fetching or while preloading the OG image. Errors render in a subtle role="alert" region below the card.

Configuration API

Each fluent method accepts a Closure for dynamic values (e.g. based on $get, $record, or $livewire).

variant(string|Closure $variant)

Visual style shared with FlexTextInput.

size(string|ControlSize|Closure $size)

Control height. See Control size. Default: md.

preview(bool|Closure $condition = true)

Enable or disable the preview card entirely. When false, the field behaves as a styled URL input only.

previewDebounce(int|Closure $milliseconds)

Delay after typing before the client calls the scrape endpoint. Default: 500. Pass 0 for immediate fetch (use sparingly).

previewMinUrlLength(int|Closure $length)

Minimum resolved URL character length before scraping starts. Default: 10. Enforced minimum: 4.
Useful with prefixes — the check runs against the full resolved URL, not the visible suffix alone.

previewMinSkeletonMs(int|Closure $milliseconds)

Minimum skeleton display time on initial card reveal (SSR-prefilled URLs or first client fetch). Default: 500. Prevents flicker when metadata resolves instantly from cache.

previewLayout('horizontal'|'vertical'|'card'|Closure $layout)

Card layout. Default: horizontal. Invalid values throw InvalidArgumentException.

resolveInitialPreviewOnServer(bool|Closure $condition = true)

When true (default), the Blade view calls resolveInitialPreview() during SSR for prefilled URLs so the card can render immediately without waiting for Alpine. When false, initial preview is deferred to the client — useful on heavy forms to avoid blocking page render or duplicate scrapes.
When true (default), the domain row is an <a> opening the URL in a new tab (rel="noopener noreferrer"). When false, the domain is plain text with the same icon styling (fff-link-preview__domain--text).

visitLabel(string|Closure $label)

Accessible label for the visit link (aria-label). Default: translated filament-flex-fields::default.link_preview.visit.

visitIcon(string|BackedEnum|Htmlable|Closure|null $icon)

Icon beside the domain row. Default: GravityIcon::Paperclip.

prefix(string|Closure|null $label) / suffix(string|Closure|null $label)

Inline affix labels on the FlexTextInput track. Empty strings are treated as no affix.

placeholder(string|Closure|null $placeholder)

Inherited from Filament HasPlaceholder. Default translation: filament-flex-fields::default.link_preview.placeholder.

readOnly(bool|Closure $condition = true) / disabled(bool|Closure $condition = true)

Inherited from Filament. Read-only still shows preview for the current URL; disabled blocks interaction and scraping triggers.

focusOutline(bool|Closure $condition = true)

Inherited from HasFieldFocusOutline. Default: false. When true, shows the shared --fff-field-focus-* ring on the input shell.

Public helper methods

resolveInitialPreview() returns null when preview is disabled, URL is empty, URL is not scrapable, or scrape returns no metadata.

Package configuration

Global scrape behaviour in config/filament-flex-fields.php: Publish config:
Example .env:
The client also keeps an in-memory cache and in-flight deduplication (url-meta-scrape.js) so repeated keystrokes do not spam the server. Scrape endpoint (named route): filament-flex-fields.url-meta.scrape.

Validation


Model & database examples

Editing an existing record with SSR preview:

Recipe: prefixed marketing domain

Recipe: read-only audit display

Recipe: heavy admin form — defer SSR scrape

Recipe: reactive live() — drive sibling fields from preview URL

Use live() when other form fields should react to URL changes. Preview fetching still debounces independently via previewDebounce().

Recipe: affix-only internal paths (preview disabled)

For intranet or relative paths where server scraping is not useful — URL input only, no preview card:
When users paste slow or rate-limited URLs, reduce churn and keep the domain as plain text:
Scrape failures render in fff-link-preview__error with role="alert"; they do not block form submission when nullable / url rules pass.

Accessibility

  • Preview card container uses aria-live="polite" for loading and reveal updates
  • When revealed, aria-label on the card reflects the scraped page title
  • Visit link uses visitLabel() as aria-label
  • Scrape errors use role="alert"
  • Input remains a standard Filament field with label / hint / error association

CSS classes

Shares FlexTextInput shell classes (fff-flex-text-input, fff-flex-text-input__shell, variant modifiers).

Assets

Lazy-loaded stylesheets (via FlexFieldAssets::stylesheetsFor('link-preview-field')):
  • flex-text-input
  • link-preview-field
Alpine component: link-preview-field (loaded with x-load). Uses wire:ignore on the Alpine root — prefer changing Livewire state or sibling fields over direct DOM manipulation in tests.

Implementation notes

  • Metadata is scraped server-side via UrlMetaScraper (Open Graph + fallback <title> / <meta name="description"> / <meta property="og:image">).
  • Non-scrapable URLs (invalid scheme, localhost, etc.) skip preview quietly.
  • Image preload runs client-side before revealing the card to avoid layout pop-in.
  • Invalid variant() or previewLayout() values throw InvalidArgumentException at render time.

Playground

Slug: link-preview-field /admin/flex-fields-playground/link-preview-field — see Playground.