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.
Place the <x-og-image> component anywhere in your Blade 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>
The component outputs a hidden <template> tag (natively invisible in browsers) in the page body. The package middleware automatically 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>
<body>
<template data-og-image>
<div class="w-full h-full bg-blue-900 text-white flex items-center justify-center">
<h1 class="text-6xl font-bold">My Post Title</h1>
</div>
</template>
</body>
The image URL contains a hash of your HTML content. When the content changes, the hash changes, so crawlers pick up the new image automatically.
The meta tags always point to /og-image/{hash}.jpeg. When that URL is first requested, the package generates the screenshot and serves it directly. The response includes Cache-Control headers, so CDNs like Cloudflare cache the image automatically.
If your views already have og:image, twitter:image, or twitter:card meta tags, remove them. The package handles these automatically. Keep any other OG meta tags you have (og:title, og:description, og:type, article:published_time, etc.). The package only manages the image-related tags.
Instead of writing your OG image HTML inline, you can reference a Blade view:
<x-og-image view="og-image.post" :data="['title' => $post->title, 'author' => $post->author->name]" />
The view receives the data array as its 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 useful when you reuse the same OG image layout across multiple pages or when the template is complex enough that you want it in its own file.
If you already have an OG image URL (for example, a custom designed image from a CMS), you can pass it directly using the url attribute:
[@if](https://github.com/if)($post->og_image)
<x-og-image :url="$post->og_image" />
[@else](https://github.com/else)
<x-og-image view="og-image.post" :data="['title' => $post->title]" />
[@endif](https://github.com/endif)
When a url is provided, the package skips screenshot generation entirely and injects the given URL in the og:image and twitter:image meta tags. This is useful when you want the generated OG image as a fallback, but prefer a hand crafted image when one is available.
By default, images are generated as JPEG. You can specify a different format:
<x-og-image format="webp">
<div class="w-full h-full bg-gradient-to-r from-purple-500 to-pink-500 text-white flex items-center justify-center">
<h1 class="text-6xl font-bold">{{ $title }}</h1>
</div>
</x-og-image>
Append ?ogimage to any page URL to see exactly what will be screenshotted. This renders just the template content at the configured dimensions (1200×630 by default), using the page's full <head> with all CSS and fonts.
w-full h-full on your root element to fill the entire viewportflex or grid for layout?ogimage appendedHow can I help you explore Laravel packages today?