Laravel Auto Generate Thumbnails for Your Blog Posts

By Priyash Patil | 5 min read | Updated: May 27, 2023 11PM UTC

Creating and uploading thumbnails for each blog post can be time-consuming, but fear not! With Laravel, a powerful PHP framework, you can automate the process of generating thumbnails for your blog posts. In this blog post, we’ll explore how to leverage Laravel to generate thumbnails effortlessly using wkhtmltoimage and laravel-snappy.

Preview

Here’s the example preview of the generated thumbnail image shared on social media:

A screenshot of shared image

How will it work?

The flow of the image generation process would be:

  1. Using Laravel’s blade view to build the HTML output.
  2. Then we use that HTML and pass it to wkhtmltoimage library through laravel-snappy the package. Which can return the binary data of a rendered image.
  3. We then use that data to store it as an image on desired file storage.

Installing Laravel Snappy

Laravel Snappy is an open-source package developed by Barry vd. Heuvel. This package is built as a wrapper for Laravel on package Snappy. Snappy uses wkhtmltopdf and wkhtmltoimage libraries for the generation of PDFs and Images.

Install the Laravel Snappy package by running the following command.

composer require barryvdh/laravel-snappy

Then publish the snappy config file using the following command:

php artisan vendor:publish --provider="BarryvdhSnappyServiceProvider"

This will publish the snappy config file at config/snappy.php. Update the file as per below:

<?php

return [
    'pdf' => [
        'enabled' => false,
        'binary'  => env('WKHTML_PDF_BINARY', '/usr/bin/wkhtmltopdf'),
        'timeout' => false,
        'options' => [],
        'env'     => [],
    ],

    'image' => [
        'enabled' => true,
        'binary'  => env('WKHTML_IMG_BINARY', '/usr/bin/wkhtmltoimage'),
        'timeout' => false,
        'options' => [],
        'env'     => [],
    ],

];

Notice we are updating the binary paths to be configurable by .env file.

Configuring wkhtmltoimage for local and production use

For this post, we are interested in the wkhtmltoimage library. This library comes bundled with wkhtmltopdf. Whether you are running the application locally or in production. You’ll need to install the wkhtmltopdf library. You can download the wkhtmltopdf library from wkhtmltopdf.org.

During the installation keep a note of the library’s installation location.

Windows

Download the suitable installer for your system and follow the installer steps to install the library. You can download the Windows install from here: Download Windows Install

Mac

You can install the library using HomeBrew. Run the following command:

brew install wkhtmltopdf

Linux

On Ubuntu systems, you can install the library by running:

sudo apt-get install -y wkhtmltopdf

Docker and Laravel Sail

If you are running the application in docker make sure to add the install statement in the docker build file. Also, if you are using Laravel Sail then you will need to customize the docker files. To do so run the following commands.

sail artisan sail:publish

This will publish the docker build file in your project root. Then you can update the specific Dockerfile and add the following line under:

RUN apt-get update && apt-get install -y wkhtmltopdf

After the installation, the usual library locations are:

After the installation update the .env with binary path variables:

WKHTML_PDF_BINARY=/usr/local/bin/wkhtmltopdf 
WKHTML_IMG_BINARY=/usr/local/bin/wkhtmltoimage

The var WKHTML_PDF_BINARY is not required for the scope of this post but can add it for later use.

Prepare Image/HTML template

Now the libraries are configured. We can work on the template from which HTML will be rendered as an Image.

Create a view file at resources/views/templates/simple.blade.php. Then add the following code.

<!DOCTYPE html>
<html lang="en">

<head>
    <title>Featured Image</title>        
    <style>
        html body {
            box-sizing: border-box;
            font-size: 16px;
        }

        *,
        *:before,
        *:after {
            box-sizing: inherit;
            margin: 0;
            padding: 0;
            font-weight: normal;
            line-height: normal;
        }

        .featured-container {
            width: 1200px;
            height: 628px;
            display: flex;
            align-items: center;
            position: relative;
        }

        .background {
            background-image: url('https://cdn.priyashpatil.com/polka-pattern.jpg');
            background-repeat: repeat-x;
            background-size: auto 100%;
            position: absolute;
            width: 100%;
            height: 100%;
            top: 0;
            left: 0;
            z-index: -1;
            fill-opacity: 0.8;
        }

        .content-padding {
            padding: 80px;
        }

        .head {
            font-size: 60px;
            font-weight: bold;
        }
    </style>
</head>

<body>
    <div class="featured-container content-padding">
        <h1 class="head">{{ $title }}</h1>
        <div class="background"></div>
    </div>
</body>

</html>

You have to keep the templates simple and less dependent on external resources to avoid any rendering issues. Also do note that some CSS properties do not work well with wkhtmltopdf so, you have to try and test the template to suit your needs.

Generating and Storing the image

For this post let’s assume you have a blog post Model with a title property and you want to generate and update the post instance. Let’s say we have and post endpoint /admin/blog/{blogPost}/generate-thumbnail which you can trigger using a simple form in the admin dashboard. For example:

routes/web.php

<?php

use AppHttpControllersPostFeaturedImageGenerateController; 

Route::post('/admin/blog/{blogPost}/generate-thumbnail', PostThumbnailGenerateController::class)->name('admin.posts.generate-thumbnail');

Then create an invokable controller as appHttpControllersPostThumbnailGenerateController.php:

<?php

namespace AppHttpControllers;

use AppModelsPost;
use BarryvdhSnappyFacadesSnappyImage;
use IlluminateHttpRequest;
use IlluminateSupportFacadesStorage;
use IlluminateSupportStr;

class PostThumbnailGenerateController extends Controller
{
    /**
     * Handle incoming request.
     */
    public function __invoke(Request $request,Post $blogPost)
    {
        // Prep new image
        $fileName = strtolower((string) Str::ulid());
        $snappyImage = SnappyImage::loadView('templates/simple', ['title' => $blogPost->title]);

        // Save generated image
        $filePath = "thumbnails/{$fileName}.jpeg";
        Storage::disk('public')->put($filePath, $snappyImage->output());

        // Update blog post
        $blogPost->thumbnail = Storage::disk('public')->url($filePath);
        $blogPost->save();

        return redirect()->back();
    }
}

Then in your admin panel, you can add the form like so:

<form action="{{ route('admin.posts.generate-thumbnail', $post->id) }}" method="POST">
    @csrf
    <button type="submit" class="btn btn-sm btn-secondary">Generate Featured Image</button>
</form>

Adding thumbnail meta

Add the meta tags in your root layout add follows:

<!doctype html>
<head>
    {{-- ... --}}
 
    <meta property="og:image" content="{{ $post->thumbnail }}">
    <meta property="twitter:image" content="{{ $post->thumbnail }}">
</head>

Rendering images directly during template development

During the development of the template, you may want to directly render the image into the browser instead of storing it in the filesystem. Snappy offers a couple of methods for it.

You can return images in browsers using the following methods:

$snappyImage = SnappyImage::loadView('templates/simple', ['title' => $blogPost->title]);
return $snappyImage->inline();

or

$snappyImage = SnappyImage::loadView('templates/simple', ['title' => $blogPost->title]);
return $snappyImage->stream();

Conclusion

Congratulations on successfully generating thumbnails based on the blog title! This post demonstrates an efficient method using the snappy package to generate captivating thumbnail images. By incorporating the Lottery Helper class, you can expand your options by including additional templates and adding a touch of randomness to the selection process.

Furthermore, you have the flexibility to automate the thumbnail generation process upon saving or updating your blog. Feel free to unleash your creativity and explore endless possibilities for creating visually appealing thumbnails that perfectly complement your content. Let your imagination soar and continue to innovate!

Keep the Conversation Going

I hope you found this post helpful! If you have any questions or feedback, feel free to reach out. You can also find me on X (Twitter) @priyashpatil for additional insights and updates on my latest content.

Related

Featured on laravel-news.com and benjamincrozat.com.

Optimized image uploads with CKEditor and Laravel

By Priyash Patil on Friday, 03 November 2023

Bootstrap 5 Remove Unused CSS with Vite and PurgeCSS

By Priyash Patil on Wednesday, 17 January 2024

Laravel Vite Deploy Assets to Global CDN

By Priyash Patil on Friday, 19 January 2024

Laravel file upload with validation example

By Priyash Patil on Thursday, 25 January 2024