spatie/laravel-og-image
Generate Open Graph images in Laravel from Blade-defined HTML. Automatically renders screenshots, serves them from a route, and caches files. Templates reuse your app’s CSS, fonts, and Vite assets—no external API required.
When you share a link on social media, platforms like Twitter, Facebook, and LinkedIn display a preview image. These are called Open Graph (OG) images. This package lets you define that image as HTML in your Blade views, and automatically screenshots it to generate the actual image file.
There are three stages to understand:
<head>Let's walk through each stage.
When a visitor (or a crawler) loads your page, two things happen:
<x-og-image> Blade component hashes the template HTML to produce a unique key (e.g. a1b2c3d4e5f6), stores the current page URL in cache keyed by that hash, and outputs a hidden <template> tag in the page body<template> tag and injects og:image, twitter:image, and twitter:card meta tags into the <head>The HTML in the response looks like this:
<head>
<!-- your existing head content -->
<meta property="og:image" content="https://yourapp.com/og-image/a1b2c3d4e5f6.jpeg">
<meta name="twitter:image" content="https://yourapp.com/og-image/a1b2c3d4e5f6.jpeg">
<meta name="twitter:card" content="summary_large_image">
</head>
<body>
<template data-og-image>
<div class="...">My Post Title</div>
</template>
</body>
The <template> tag is natively invisible in browsers, so visitors don't see it. The meta tags point to a route in your app (/og-image/a1b2c3d4e5f6.jpeg). The image doesn't exist yet, but that's fine. Crawlers will request it next.
When Twitter, Facebook, or LinkedIn sees the og:image meta tag, it makes a request to https://yourapp.com/og-image/a1b2c3d4e5f6.jpeg. The a1b2c3d4e5f6 part is a hash generated from the HTML content of your OG image template. When you change the template content, the hash changes, producing a new URL. Here's what happens:
OgImageController, which checks if the image already exists on disk?ogimage appended. This is a separate internal HTTP request to your appRenderOgImageMiddleware detects the ?ogimage parameter and replaces the response with a minimal HTML page: just your page's <head> (preserving all CSS, fonts, and Vite assets) and the template content, displayed at 1200x630 pixelspublic)Cache-Control headers. For remote disks (S3, etc.), a 301 redirect is issued to the storage URLBecause the screenshot uses your actual page's <head>, your OG image inherits all of your CSS, fonts, and Vite assets. No separate stylesheet configuration needed.
Once the image exists on disk, subsequent requests to /og-image/a1b2c3d4e5f6.jpeg serve the image directly without taking another screenshot. The meta tags always use the same stable /og-image/{hash}.{format} URL, which makes this work well with page caching and CDNs like Cloudflare.
The /og-image/ route is designed to be fast and CDN-friendly:
Cache-Control headers, so CDNs cache the responseHow can I help you explore Laravel packages today?