Auto Generate Open Graph Images With Laravel

Paulund - May 28 - - Dev Community

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
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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)
    {

    }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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');
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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',
]);
Enter fullscreen mode Exit fullscreen mode

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',
    ]);
}
Enter fullscreen mode Exit fullscreen mode

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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">
Enter fullscreen mode Exit fullscreen mode

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.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .