4 min read

Auto-Generate Branded Social Share Images in Laravel

Branded social share image template showing a post title and corner logo generated automatically in Laravel

I'm always exploring new ways to enhance user experience and improve content visibility on social media. Social media platforms like X (formerly Twitter), LinkedIn, and Facebook prominently display shared content images, which can directly affect click-through rates. My goal was to automatically create attractive, branded, consistent, professional-looking social share images—without the tedious manual effort.

Tooling and Setup

I was already using Laravel and Spatie Media Library to manage my blog content and media uploads, which made integration seamless. The star of this solution, however, is the Intervention Image library. Its simplicity and expressive API made it incredibly easy to create, manipulate, and layer images dynamically—perfect for generating polished, on-brand social share images.

Here's the stack I used:

  • Laravel for backend structure

  • Spatie Media Library for media handling

  • Intervention Image for all the heavy lifting in image generation

Step-by-Step Implementation

Creating the Service Class

The core logic lives in a service class: SocialShareImageService. It’s responsible for composing and saving the image. Here's a breakdown of how it works:

public function generateForPost(Post $post): string
{
    $image = Image::create(1200, 630);
    $image->fill('#0F172A');

    $featuredImage = $post->getFirstMediaPath('default', 'featured');
    if ($featuredImage) {
        $featured = Image::read($featuredImage);
        $featured->cover(1200, 630)
        ->brightness(5)
        ->contrast(10);
        
        $image->place($featured);
    }

This sets up a 1200x630 canvas (ideal for social sharing), optionally adds a featured image with enhancements, and prepares the base design.

Styling with Overlays and Text

To improve legibility and reinforce branding, overlays and text elements are added:

// Add a dark gradient overlay for better contrast
$this->addProfessionalOverlay($image);

// Add the post title
$image->text($post->post_title, 100, 250, function (FontFactory $font) {
    $font->file(public_path('fonts/Inter-Bold.ttf'));
    $font->size(64);
    $font->color('#FFFFFF');
    $font->align('left');
    $font->valign('top');
    $font->wrap(1000);
});

private function addProfessionalOverlay($image): void
    {
        // Create dark overlays with varying opacity for a gradient-like effect

        // Left side overlay (darker on left for text area)
        for ($i = 0; $i < 8; $i++) {
            $width = 100;
            $leftOverlay = Image::create($width, 630);
            $leftOverlay->fill('#000000');
            $image->place($leftOverlay, 'top-left', offset_x: $i * $width, opacity: 80 - ($i * 10));
        }

        // Bottom overlay (darker at bottom for brand information)
        for ($i = 0; $i < 5; $i++) {
            $height = 40;
            $bottomOverlay = Image::create(1200, $height);
            $bottomOverlay->fill('#000000');
            $image->place($bottomOverlay, 'bottom-left', offset_y: 630 - ($i + 1) * $height, opacity: 70 - ($i * 15));
        }

        // Add a subtle vignette effect
        $this->addVignetteEffect($image);
    }

These gradients improve contrast, while the title is styled for readability using the Inter font. You can also optionally include a publication date and website URL for additional context.

Branding the Image

We include a decorative small corner accent for subtle but clear branding:

// Corner badge with initials
$this->addCornerBranding($image);

private function addCornerBranding($image): void  
{  
    $size = 80;  
    $cornerX = 1120;  
    $cornerY = 550;  
  
    $square = Image::create($size, $size);  
    $square->fill('#6B4B96');  
    $image->place($square, 'top-left', offset_x: $cornerX, offset_y: $cornerY, opacity: 80);  
  
    // Add text or logo inside  
    $image->text(  
        'MW',  
        $cornerX + ($size / 2),  
        $cornerY + ($size / 2) + 5,  
        function (FontFactory $font): void {  
            $font->file(public_path('fonts/Inter-Bold.ttf'));  
            $font->size(32);  
            $font->color('#FFFFFF');  
            $font->align('center');  
            $font->valign('middle');  
        }  
    );  
}

Inside addCornerBranding(), we create a square and place text in the center for a custom logo effect, giving the image a distinctive signature.

Saving the Image

Finally, the image is saved with a unique name. This filename is generated by hashing the post's ID and title:

$filename = 'social-share-' . md5($post->id . '-' . $post->post_title) . '.jpg';
Storage::disk('public')->put("social-share/$filename", $image->toJpeg(95)->toString());

Hashing ensures consistent filenames for unchanged posts. If a title changes, the hash changes too—automatically triggering a new image to be generated.

Using a Trait for Easy Access

The HasSocialShareImage trait allows any model (like Post) to use the image generator easily:

public function getSocialShareImageUrl(): ?string
{
    if (Storage::disk('public')->exists($path)) {
        return Storage::disk('public')->url($path);
    }
    return app(SocialShareImageService::class)->generateForPost($this);
}

This approach keeps the logic centralized, making it easy to reuse across your Laravel application.

Example & Source Code

Want to see it in action? Here's a sample social share image generated by the service:

You can view the full source code for this implementation on GitHub Gist:

👉 View Gist: SocialShareImageService + Trait

Final Thoughts

This automated solution significantly reduces manual workload while improving brand consistency across social media channels. It's a great example of how Laravel’s flexibility and Intervention Image’s power can be combined to enhance digital content effectively.

Feel free to adapt or expand this solution for your own Laravel projects!


Since the writing of this article I've found a open source package that that will build open graph images for you for you called Open Graphy. It handles the image generation a bit differently requiring you to have a chrome browser installed on your server but it is pretty cool no the less.