Generate OG images for your Laravel app original
When you share a link on Twitter, Facebook, or LinkedIn, the platform shows a preview image. Getting those Open Graph images right usually means either using an external service or setting up a separate rendering pipeline. We just released laravel-og-image, a package that lets you define your OG image as HTML right inside your Blade views. The package takes a screenshot of that HTML and serves it as the OG image. No external API needed, everything runs on your own server.
Let me walk you through what the package can do.
Getting started
Install the package via Composer:
composer require spatie/laravel-og-image
The package uses spatie/laravel-screenshot under the hood, which requires Node.js and Chrome/Chromium on your server. If you prefer not to install those, you can use Cloudflare's Browser Rendering API instead (more on that later).
The package automatically registers middleware in the web group, so there's no manual configuration needed. Just drop the Blade component into your view:
<x-og-image> <div class="w-full h-full bg-blue-900 text-white flex items-center justify-center"> <h1 class="text-6xl font-bold">{{ $post->title }}</h1> </div> </x-og-image>
That's all you need. The component outputs a hidden <template> tag in the page body, and the middleware injects the og:image, twitter:image, and twitter:card meta tags into the <head>:
<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>
The image URL contains a hash of the HTML content. When you change the template, the hash changes, so crawlers automatically pick up the new image.
How it works
The clever bit is that your OG image template lives on the actual page, so it inherits your page's existing CSS, fonts, and Vite assets. No separate stylesheet configuration needed.
Here's what happens when a crawler requests the image:
- The request hits the package's controller at
/og-image/{hash}.jpeg - The controller looks up the original page URL from cache (stored there by the Blade component during rendering)
- Chrome visits that page with
?ogimageappended - The middleware detects the
?ogimageparameter and replaces the response with a minimal HTML page: just the<head>(preserving all CSS and fonts) and the template content at 1200x630 pixels - Chrome takes a screenshot and saves it to disk
- The image is served back to the crawler with
Cache-Controlheaders
Subsequent requests serve the image directly from disk. The route runs without sessions, CSRF, or cookies, and the content-hashed URLs play nicely with CDNs like Cloudflare.
You can preview any OG image by appending ?ogimage to the page URL. This is really useful while designing your templates.
Using a Blade view
Instead of writing the HTML inline, you can reference a separate Blade view:
<x-og-image view="og-image.post" :data="['title' => $post->title, 'author' => $post->author->name]" />
The view receives the data array as variables:
{{-- resources/views/og-image/post.blade.php --}} <div class="w-full h-full bg-blue-900 text-white flex items-center justify-center p-16"> <div> <h1 class="text-6xl font-bold">{{ $title }}</h1> <p class="text-2xl mt-4">by {{ $author }}</p> </div> </div>
This is handy when you reuse the same layout across multiple pages or when the template gets complex enough that you want it in its own file.
Fallback images
Pages that don't use the <x-og-image> component won't get any OG image meta tags by default. You can register a fallback in your AppServiceProvider:
use Illuminate\Http\Request; use Spatie\OgImage\Facades\OgImage; public function boot(): void { OgImage::fallbackUsing(function (Request $request) { return view('og-image.fallback', [ 'title' => config('app.name'), 'url' => $request->url(), ]); }); }
The closure receives the full Request object, so you can use route parameters and model bindings to customize the image. Return null to skip the fallback for specific requests. Pages that do have an explicit <x-og-image> component are never affected by the fallback.
Customizing screenshots
You can configure the image size, format, quality, and storage disk via the OgImage facade in your AppServiceProvider:
use Spatie\OgImage\Facades\OgImage; OgImage::format('webp') ->size(1200, 630) ->disk('s3', 'og-images');
By default, images are generated at 1200x630 with a device scale factor of 2, resulting in crisp 2400x1260 pixel images. You can also override the size per component:
<x-og-image :width="800" :height="400"> <div>Custom size OG image</div> </x-og-image>
If you don't want to install Node.js and Chrome on your server, you can use Cloudflare's Browser Rendering API instead:
OgImage::useCloudflare( apiToken: env('CLOUDFLARE_API_TOKEN'), accountId: env('CLOUDFLARE_ACCOUNT_ID'), );
Pre-generating images
By default, images are generated lazily on the first crawler request. If you'd rather have them ready ahead of time, you can pre-generate them with an artisan command:
php artisan og-image:generate https://yourapp.com/page1 https://yourapp.com/page2
Or programmatically, which is useful for generating the image right after publishing content:
use Spatie\OgImage\Facades\OgImage; class PublishPostAction { public function execute(Post $post): void { // ... publish logic ... dispatch(function () use ($post) { OgImage::generateForUrl($post->url); }); } }
In closing
Our og image package is already running on the blog you're reading right now. You can see the pull request that added it to freek.dev if you want a real-world example of how to integrate it. Try appending ?ogimage to the URL of any post on this blog to see which image would be generated for that post.
With this package, your OG images are just Blade views. You design them with the same Tailwind classes, fonts, and assets you already use in the rest of your app. No separate rendering setup, no external API, no manual meta tag management.
You can find the full documentation on our documentation site and the source code on GitHub.
The approach of using a <template> tag to define OG images inline with the page's own CSS is inspired by OGKit by Peter Suhm. If you'd rather not self-host the generation of OG images, definitely check out OGKit.
This is one of the many packages we have created at Spatie. If you want to support our open source work, consider picking up one of our paid products.