Skip to main content
Enter a URL and show its page title, description and image without leaving your form. Link Preview uses the same input shell, sizes and appearance as Flex Text Input. The database stores the URL, not downloaded HTML or preview images.

Three preview layouts

Choose a compact horizontal preview, a vertical preview or a full-width image card.

Responsive loading

Requests run after input changes. Cached previews can appear on the first render without a remote request.

First field

Publish the package assets as described in Installation. Authenticated routes are registered automatically from the component definition. No queue worker or additional frontend build is required for previews.

Layout and appearance

The image sits beside the title, description and domain.

Every field option

Fluent options accept closures for values that depend on the record or form. Standard Filament methods such as label(), helperText(), required(), live(), afterStateUpdated() and columnSpanFull() remain available.

Practical examples

Enter only a path

Pasting a full matching URL removes the duplicated visible prefix.

Smaller input, quieter preview

URL input without remote requests

A valid private URL may be stored. Private hosts cannot be fetched for previews.

Read-only record

React to URL changes

Preview debounce and Livewire update debounce are independent settings.

Validation and storage

The server validates a nullable string of at most 2048 characters, with an HTTP or HTTPS scheme, no embedded credentials, and the default HTTP/HTTPS port. Arrays, arbitrary text, javascript: URLs and unsupported ports are rejected. required() makes empty values invalid. Whitespace is trimmed; empty values save as null. A preview failure does not make an otherwise valid URL unsaveable. Pages may omit metadata, block automated requests, require a login, or exceed the download limit.
No model cast is required. Authorization to edit the record remains your application’s responsibility.

Global configuration

In config/filament-flex-fields.php, configure link_preview:
Named routes: filament-flex-fields.url-meta.scrape and filament-flex-fields.url-meta.image. Use route names rather than hardcoded paths. The Laravel cache store must support atomic locks. Shared production cache also shares rate limits and metadata across application instances.

Loading and safety

  • CSS and JS belong to each rendered field’s asset boundary; closed tabs and modals wait for activation.
  • Requests for the same URL and endpoint share in-flight work and a bounded browser cache.
  • Changing the URL cancels the previous request; stale responses cannot replace the new preview.
  • Removing the field releases its watchers, listeners, timers and pending image work.
  • The server checks every redirect destination, rejects private/reserved addresses and pins DNS for the connection.
  • Remote requests carry no application session cookies or authorization headers.
  • Metadata transfers stop after the HTML head, or at the 256 KiB capture limit. Comments, script text and quoted attributes cannot masquerade as the head boundary. Redirect bodies are skipped. Image responses are limited to 2 MiB.
  • Preview images use a short-lived signed local URL, stable within a five-minute interval so shared images can use the browser cache. The image endpoint revalidates redirects, accepts only supported raster images, and caps dimensions at 8 Mi pixels. SVG and HTML are rejected.

Metadata selection

The parser reads a bounded HTML head without executing scripts or downloading linked structured-data documents. Title selection uses Open Graph, Twitter, HTML title and then JSON-LD. Description selection uses Open Graph, Twitter, standard description/abstract tags and then JSON-LD. Image selection preserves the first usable Open Graph image group, prefers its secure alternate, then tries subsequent groups, Twitter, image metadata, JSON-LD and supported link declarations. Empty, malformed and syntactically private image candidates do not prevent trying a later candidate. Network availability and actual raster safety are checked by the protected image endpoint. JSON-LD supports WebPage, Article, NewsArticle, BlogPosting, Product and VideoObject, including @graph, image objects and image lists. Up to 16 scripts of 64 KiB each are examined with bounded graph traversal; malformed JSON is ignored. Body-only metadata and client-rendered metadata are not evaluated. Declared Windows-1252/ISO-8859-1 text is converted before extraction. Open Graph image ordering follows the Open Graph protocol.

Cache and concurrent requests

Server and browser metadata caches ignore URL fragments and preserve query parameters. Cache hits avoid both DNS and remote HTTP. If another worker is already fetching the same URL, the endpoint can return 503 with Retry-After; the browser performs at most four bounded retries, cancelling them when the field is removed or its value changes. The browser cache holds at most 64 entries for five minutes and separates endpoints. Server cache TTL remains configurable. Isolated DNS uses the matching PHP CLI executable and requires proc_open. It does not use a third-party DNS-over-HTTPS service. Missing CLI/process support, malformed DNS output, mixed private/public answers and failed lookups fail closed. On other platforms, choose isolated when your deployment needs a hard DNS process deadline; verify CLI availability as part of deployment. The preview card announces updates politely; failures use an accessible alert. Input styling supports light/dark themes and logical text alignment for RTL.

Troubleshooting

Check that the destination is public and serves HTML with title/Open Graph/Twitter metadata. Private addresses, authenticated pages and oversized responses cannot be previewed. A URL can remain valid for storage even when no preview is available.
Verify the configured authentication guard and logged-in session. Image URLs expire within five to ten minutes; requesting fresh metadata creates a new signature. Do not remove signing or authentication middleware.
The user’s request limit was reached. Wait for the period indicated by the response’s Retry-After header, or configure an appropriate bounded limit.
Ensure the package provider is discovered and refresh the Laravel route cache after updating the package or endpoint configuration. Publish current assets so browser requests use the current route names.
See custom component routes to declare protected endpoints for your own extension, or try the Link Preview page in the live playground.
Last modified on October 16, 2026