Protone Media logo

Hey! Did you hear about Launcher? 🚀

It's an easy-to-use deployment tool to deploy your Laravel apps containerized with Docker. Launcher is remarkably easy, and still, you can fully customize it to your needs. Start launching your sites in just minutes using our free 14-day trial.

Create beautiful Open Graph images with Browsershot and Tailwind CSS

For Artisan School, I wanted to create custom Open Graph images for each video page. For example, when somebody shares a link on social media, platforms like Facebook and Twitter use the OG image to represent the shared link. There are many services to create and serve OG images, but I didn't want to depend on an external service. For the website, I already had a Laravel application up and running with Tailwind CSS.

The routes file for this app is quite simple. Based on the slug URI segment of the route, I fetch the video from the database and return a Blade view:

use App\Models\Video;

Route::get('/{slug}', function ($slug) {
    $video = Video::whereSlug($slug)->firstOrFail();

    return view('video', ['video' => $video]);
});

I added a second route, which also returns a Blade view, just to render the OG image.

Route::get('/{slug}/openGraphImage', function ($slug) {
    $video = Video::whereSlug($slug)->firstOrFail();

    return view('videoOpenGraphImage', ['video' => $video]);
})->name('video.openGraphImage');

These images are typically 1200x630 pixels, so I created a container with those dimensions and centered it on the page. Be sure to enable Tailwind's JIT mode to get the square-bracket notation to work. In this container, I added the Artisan School logo and the title of the video.

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="{{ mix('css/app.css') }}">
    </head>
    <body class="flex items-center justify-center min-h-screen">
        <div class="bg-blue-500 w-[1200px] h-[630px]">
            <!-- your content -->
        </div>
    </body>
</html>

Now how do we store an image out of this Blade view? There's a gorgeous package called Browsershot by Spatie. It converts a webpage to an image or PDF using a headless version of Google Chrome. It needs some setup, but it's straightforward to use. I added a method within my Video model that handles the conversion and saves the image to the public folder of my app.

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;
use Spatie\Browsershot\Browsershot;

class Video extends Model
{
    public function saveOpenGraphImage()
    {
        $path = Storage::disk('public')->path("{$this->slug}.jpg");

        Browsershot::url(route('video.openGraphImage', ['slug' => $this->slug]))
            ->waitUntilNetworkIdle()
            ->showBackground()
            ->windowSize(1200, 630)
            ->setScreenshotType('jpeg', 100)
            ->save($path);
    }

    public function getOpenGraphImageUrl(): string
    {
        return Storage::disk('public')->url("{$this->slug}.jpg");
    }
}

To make my life easy, I also added a method to return the full URL of the OG image. You can use this in the header section of the HTML page or with an SEO package like artesaos/seotools.

<meta property="og:image" content="{{ $video->getOpenGraphImageUrl() }}">

Related posts

Want to stay up-to-date on Laravel news and packages?

Pascal Baljet on Twitter

Follow me on Twitter: @pascalbaljet

Pascal Baljet on Twitter

Subscribe to my YouTube channel

© 2013-2021 Protone Media B.V.