Laravel is a popular PHP framework that makes it easy to build web applications. In this article, we will look at how to auto-generate Open Graph images with Laravel. If you want to use this in your application you can install the Laravel package Laravel Og Image Generator.
For this functionality we're going to generate the image in the background, cache this image in the local storage and then serve this image to the user. This way we can generate the image once and then serve it to the user without having to generate it every time.
In order to do this we're going to use the package spatie/browsershot
that will use the headless browser to generate
the image. This will allow us to create a route that the headless browser to use to store the output as a image.
Install Browsershot
The first thing you need to do is install the spatie/browsershot
package. This package is a utility for generating
images using a headless browser. You can install it using composer.
composer require spatie/browsershot
Create The Route For The Image
Once you have installed the spatie/browsershot
package, you can create a new route that will generate the Open Graph
image. This route will use the browsershot
package to generate the image and store it in the local storage.
Here is an example of how you can create a route that generates an Open Graph image:
Route::get('/og-image', OgImageController::class);
In this example, we are creating a new route that will use the OgImageController
to generate the Open Graph image.
Create The Controller
There's a few steps for this controller so we're going to break it down into a few steps.
First we need to create the controller with the invoke method that will generate the image.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class OgImageController extends Controller
{
public function __invoke(Request $request)
{
}
}
We're going to pass in a querystring into the route that we'll use as the title in the generated image. This will make the route look like this:
/og-image?title=Hello%20World
In the controller we need to access this querystring to use as the text in the image we do this by using the $request
object.
$request->validate([
'title' => 'required|string',
]);
$title = $request->get('title');
Generate The HTML From Blade
We need to get the HTML from the blade file as we're going to pass this into the browsershot package, we don't want to return the HTML in the controller but we need render the HTML.
$html = view('ogimage', [
'title' => $title,
])->render();
Create the blade file in the resources/views
directory called ogimage.blade.php
with the following content:
<!DOCTYPE html>
<html lang="en">
<head>
@vite('resources/js/app.js')
</head>
<body>
<div class="border-2 border-gray-800 w-[1200px] h-[630px] bg-gray-900">
<div class="flex flex-col items-center justify-center h-full w-full bg-cover bg-center bg-no-repeat">
<h1 class="font-bold text-4xl text-white">{{ $title }}</h1>
</div>
</div>
</body>
</html>
Notice the @vite
directive this is a custom directive that will include the Vite assets in the blade file. You will
need to either use your main CSS file or in this case we're using Javascript
to import the CSS. Use the one that fits your application.
Generate The Image
Back in the controller we can now generate the image using the browsershot
package. We're going to use
the browsershot
package to generate the image and store it in the local storage.
$slugTitle = Str::slug($title);
$browsershot = Browsershot::html($html)
->noSandbox()
->showBackground()
->windowSize(1200, 630)
->setScreenshotType('png');
if (config('ogimage.node_path')) {
$browsershot->setNodeBinary(config('chrome.node_path'));
}
if (config('ogimage.npm_path')) {
$browsershot->setNpmBinary(config('chrome.npm_path'));
}
$image = $browsershot->screenshot();
// Store image locally
Storage::disk('local')->put('public/og-images/' . $slugTitle . '.png', $image);
In this example, we take the generated HTML and pass it into the Browsershot
package. We set the window size to
1200x630 pixels and set the screenshot type to PNG. We then store the image in the local storage.
Serve The Image
Now that we have generated the image and stored it in the local storage, we can serve this image to the user. We can do this by returning the image from the controller.
return response($image, 200, [
'Content-Type' => 'image/png',
]);
Checking For Existing Image
The above code will generate the image everytime and store it in the local storage, but we don't need to generate the image everytime. We can check if the image exists and if it does we can return the image from the local storage.
if (Storage::disk('local')->exists('public/og-images/'.$slugTitle.'.png')) {
return response(Storage::disk('local')->get('public/og-images/'.$slugTitle.'.png'), 200, [
'Content-Type' => 'image/png',
]);
}
We can put this code at the top of the controller and if the image exists we can return the image from the local storage.
Full Controller Code
Below is the full code used in the controller that you can use to generate the ogimage
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Spatie\Browsershot\Browsershot;
class OgImageController extends Controller
{
public function __invoke(Request $request)
{
$request->validate([
'title' => 'required|string',
]);
$title = $request->get('title');
$slugTitle = Str::slug($title);
if ($this->hasImage($slugTitle)) {
return response($this->getImage($slugTitle), 200, [
'Content-Type' => 'image/png',
]);
}
$html = view('ogimage', [
'title' => $title,
])->render();
$browsershot = Browsershot::html($html)
->noSandbox()
->showBackground()
->windowSize(1200, 630)
->setScreenshotType('png');
if (config('chrome.node_path')) {
$browsershot->setNodeBinary(config('chrome.node_path'));
}
if (config('chrome.npm_path')) {
$browsershot->setNpmBinary(config('chrome.npm_path'));
}
$image = $browsershot->screenshot();
$this->saveImage($slugTitle, $image);
return response($image, 200, [
'Content-Type' => 'image/png',
]);
}
private function getFilePath($slugTitle)
{
return 'public/og-images/'.$slugTitle.'.png';
}
private function hasImage($slugTitle)
{
return Storage::disk('local')->exists($this->getFilePath($slugTitle));
}
private function getImage($slugTitle)
{
return Storage::disk('local')->get($this->getFilePath($slugTitle));
}
private function saveImage($slugTitle, $image)
{
Storage::disk('local')->put($this->getFilePath($slugTitle), $image);
}
}
Add Meta Tags To Your Site
In order to use this image when posting to social media we need to add the Open Graph meta tags to the site. This will tell the social media platform to use the image we generated.
<meta property="og:title" content="Hello World">
<meta property="og:image" content="/og-image?title=Hello%20World">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
In this example, we are setting the title of the post to "Hello World" and the image to the route we created earlier.
Conclusion
In this article, we looked at how to auto-generate Open Graph images with Laravel. We used the spatie/browsershot
package to generate the image and store it in the local storage. This allows us to generate the image once and then serve
it to the user without having to generate it every time.
This functionality can be useful for generating Open Graph images for blog posts, social media posts, and other content that requires an image.