Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Image Optimization in Laravel with Spatie Media Library

By Kokil Thapa | Last reviewed: August 2026

Slow pages kill conversions, and unoptimized uploads are usually the culprit. Implementing effective image optimization in Laravel with Spatie Media Library requires moving beyond simple storage to automated, queue-driven transformation pipelines that generate modern formats and responsive variants at upload time. This guide covers the exact configuration, model setup, and Blade integration patterns I use on production eCommerce and legal-tech platforms to ensure fast Core Web Vitals without manual intervention. For broader context on how these optimizations fit into a high-performance store, see my guide on building SEO-optimized ultra-fast e-commerce platforms with Laravel.

How do you configure Spatie Media Library for automated image optimization?

The foundation of reliable image optimization in Laravel with Spatie Media Library is correct package configuration. In 2026, with Laravel 12.x and PHP 8.4 as the current stable stack, you should be running Spatie Media Library v11 or higher. The default configuration works for basic storage but fails at performance because it processes images synchronously during the HTTP request cycle.

Install and publish configuration

composer require "spatie/laravel-medialibrary:^11.0"
php artisan vendor:publish --provider="Spatie\MediaLibrary\MediaLibraryServiceProvider" --tag="medialibrary-config"
php artisan migrate

After publishing, open config/media-library.php. The most critical change for production systems is setting the queue connection. Never process images synchronously in a web request; a single 5MB upload can block the response for seconds.

// config/media-library.php
'queue_name' => 'media-conversions',
'queue_connection_name' => env('MEDIA_QUEUE_CONNECTION', 'redis'),

'image_optimizers' => [
    Spatie\ImageOptimizer\Optimizers\Jpegoptim::class => [
        '-m85', '--strip-all', '--all-progressive',
    ],
    Spatie\ImageOptimizer\Optimizers\Pngquant::class => [
        '--force', '--quality=85',
    ],
    Spatie\ImageOptimizer\Optimizers\Cwebp::class => [
        '-m 6', '-q 80',
    ],
],

You must also install the underlying optimizer binaries on your server. On Ubuntu 24.04, which I use for most Nepal-based client deployments:

sudo apt update
sudo apt install jpegoptim optipng pngquant libavif-bin webp

Without these system packages, Spatie silently skips lossless compression even though your PHP configuration looks correct. I have debugged this exact issue on multiple production servers where developers assumed the Composer package included the binaries.

User UploadHTTP RequestQueue JobRedis / DatabaseWorker Processjpegoptim + cwebpWebP / AVIFResponsive SizesThumbnails
Async image optimization pipeline prevents blocking user requests during conversion

What media conversions should you define for optimal Core Web Vitals?

Defining the right conversions directly impacts Largest Contentful Paint (LCP) and Cumulative Layout Shift (CLS). A common mistake I see when auditing sites is defining too many conversions or using arbitrary pixel values. Every conversion you define generates a physical file on disk and consumes queue worker time. Be intentional.

Model-level conversion registration

use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
use Spatie\MediaLibrary\MediaCollections\Models\Media;

class Product extends Model implements HasMedia
{
    use InteractsWithMedia;

    public function registerMediaConversions(?Media $media = null): void
    {
        // Thumbnail for admin tables and cart previews
        $this->addMediaConversion('thumb')
            ->width(300)
            ->height(300)
            ->sharpen(10)
            ->format('webp')
            ->performOnCollections('images');

        // Main product display - constrained width, auto height
        $this->addMediaConversion('product')
            ->width(800)
            ->height(null)
            ->format('webp')
            ->withResponsiveImages()
            ->performOnCollections('images');

        // Hero/banner variant for featured products
        $this->addMediaConversion('hero')
            ->width(1200)
            ->height(600)
            ->fit(\Spatie\Image\Manipulations::FIT_CROP, 1200, 600)
            ->format('webp')
            ->performOnCollections('featured');
    }
}

Key decisions in this configuration:

  • Format targeting: Always specify ->format('webp') explicitly. While browsers support AVIF more broadly in 2026, WebP still offers better compatibility with older devices still in circulation in Nepal and South Asia. Generate AVIF only if your analytics confirm significant traffic from supporting browsers.
  • Responsive images: Call withResponsiveImages() only on conversions used in content areas where viewport width varies. Do not enable it for fixed-size thumbnails or avatars — it generates unnecessary files.
  • Crop strategy: Use FIT_CROP for hero banners where aspect ratio must be exact. Use unconstrained height (null) for product images to preserve natural proportions and avoid CLS.
  • Collection scoping: Always use performOnCollections() to prevent thumbnail generation on PDF documents or video files attached to the same model.

How do you render responsive images correctly in Blade templates?

Generating optimized files is only half the battle. If your Blade templates reference the wrong URLs or omit width/height attributes, you negate all performance gains. Proper rendering of image optimization in Laravel with Spatie Media Library requires disciplined markup.

Using the built-in img tag helper

<!-- Recommended: Automatic srcset, sizes, and dimensions -->
{{ $product->getFirstMedia('images')?->img('product')
    ->attributes(['class' => 'w-full h-auto rounded-lg', 'loading' => 'lazy']) }}

<!-- Manual control when needed -->
@php $media = $product->getFirstMedia('images'); @endphp
@if($media)
    <img
        src="{{ $media->getUrl('product') }}"
        srcset="{{ $media->getSrcset('product') }}"
        sizes="(max-width: 768px) 100vw, 800px"
        width="{{ $media->getWidth('product') }}"
        height="{{ $media->getHeight('product') }}"
        alt="{{ $media->alt ?? $product->name }}"
        loading="lazy"
        decoding="async"
        class="w-full h-auto rounded-lg"
    >
@endif

Critical details often missed:

  1. Always include width and height attributes. Even with responsive CSS, the browser needs intrinsic dimensions to reserve space before the image loads. Missing these is the #1 cause of CLS on Laravel sites I audit.
  2. Use meaningful alt text. Pull from the media object’s alt field, falling back to the model’s name. This matters for accessibility and on-page SEO. For legal-tech portals like those I build for Nepal law firms, descriptive alt text on document previews improves both compliance and search visibility.
  3. Set loading="lazy" on below-fold images. Never lazy-load above-the-fold LCP images. For product detail pages, the first product image should use loading="eager" and include a <link rel="preload"> in the head.
  4. Use decoding="async". This prevents image decoding from blocking the main thread during initial paint.
❌ Incorrect MarkupNo width/height reservedImage loads → layout shiftsHigh CLS Score✓ Correct Markupwidth="800" height="600"Space reserved before loadImage fills reserved spaceZero CLS Impact
Explicit dimensions prevent cumulative layout shift during image loading

When should you regenerate conversions after changing media settings?

Configuration changes inevitably happen mid-project. You add a new conversion, adjust dimensions, or switch from JPEG to WebP. Existing media files retain their old conversions unless you explicitly regenerate them. This is a frequent source of "it works locally but not in production" bugs.

Safe regeneration workflow

# Regenerate ALL conversions (full site rebuild)
php artisan media-library:regenerate

# Regenerate only specific conversion names
php artisan media-library:regenerate --only=product,thumb

# Regenerate for a specific model type
php artisan media-library:regenerate --model-type="App\Models\Product"

# Regenerate only missing conversions (safe incremental)
php artisan media-library:regenerate --only-missing

# Force regeneration including existing files
php artisan media-library:regenerate --force

In practice, always start with --only-missing on production. Full regeneration on a site with thousands of media items can take hours and consume significant CPU/memory. Run it via your deployment pipeline or as a scheduled job during low-traffic periods, never during peak hours.

For projects deployed with Deployer 7 and GitLab CI — the setup I use for multiple sister sites sharing infrastructure — add regeneration as a post-deploy task gated behind a flag:

// deploy.php
task('media:regenerate', function () {
    if (get('regenerate_media', false)) {
        run('{{bin/php}} {{release_path}}/artisan media-library:regenerate --only-missing');
    }
})->desc('Regenerate missing media conversions');

This prevents accidental full rebuilds on routine deploys while making intentional regeneration a single variable toggle.

How does Spatie Media Library compare to alternative image optimization approaches?

Spatie Media Library is not the only option. Understanding trade-offs helps you choose correctly for your project’s constraints.

CriteriaSpatie Media LibraryIntervention Image + CustomCloudinary / Imgix
Setup complexityModerate (package + system binaries)High (build entire pipeline)Low (API-only)
Ongoing costServer resources only (~NPR 2,000–5,000/mo VPS)Server resources onlyUsage-based ($29–$224+/mo USD)
Queue integrationBuilt-in, configurableManual implementationN/A (processed on CDN edge)
Responsive imagesAutomatic srcset generationManual breakpoint logicAutomatic with URL params
Offline/local devFull functionalityFull functionalityRequires internet + API key
Data sovereigntyFiles stay on your serverFiles stay on your serverFiles stored on vendor CDN
Best forSME apps, legal-tech, Nepal-hosted sitesSimple single-purpose transformsHigh-traffic global SaaS, media-heavy apps

For most Nepal-based businesses and SME clients I work with, Spatie Media Library hits the sweet spot: no recurring USD costs, data stays local, and the feature set covers real production needs. Cloud services make sense only when traffic volume justifies the expense or when you need global edge caching that your own infrastructure cannot provide. If you are evaluating hosting options alongside this decision, review cloud hosting services in Nepal for local pricing context.

Start: Need Image Optimization?Budget > $50 USD/month?NoYesData must stay local?Global CDN needed?YesNoSpatie Media LibraryBest for Nepal SMEsCloudinary / ImgixIf budget allowsCloudinary / ImgixEdge processing wins
Decision framework for selecting an image optimization strategy based on project constraints

Implement Image Optimization in Laravel with Spatie Media Library Today

Effective image optimization in Laravel with Spatie Media Library comes down to three non-negotiable practices: always process conversions via queue workers, define only the conversions your templates actually use, and render with explicit dimensions plus responsive srcsets. Skip any of these and you leave performance on the table or introduce layout instability. Start with the configuration and model patterns shown above, verify your system binaries are installed, and test with --only-missing regeneration before touching production. If you need help implementing this on an existing Laravel application or want a technical audit of your current media pipeline, get in touch to discuss your project.

Frequently Asked Questions

Spatie Media Library is a Laravel package that manages file uploads and generates optimized image conversions automatically. It handles resizing, format conversion, and responsive sets without custom intervention code.

The core media library package is free and open source under MIT license. Paid add-ons like Media Library Pro offer UI components for roughly USD 199 (NPR 26,500) per project but are optional for backend optimization.

Version 11 requires PHP 8.2 or higher and Laravel 10, 11, or 12. Always check composer requirements before upgrading production systems running older PHP 8.1 environments.

Register the OptimizesConversions trait on your model and define conversion methods using registerMediaConversions. Configure quality settings between 60-80 for web delivery to balance visual fidelity against file size reduction effectively.

Yes, define performOnCollections and addFormat conversions for webp or avif in your model. Ensure your server has GD or Imagick extensions compiled with modern codec support, which most Ubuntu 22/24 PHP-FPM installations include by default.

Always queue conversions for production applications handling user uploads. Synchronous processing blocks HTTP requests during resize operations. Configure a dedicated queue worker with sufficient memory limits since large image transformations can consume 256MB or more RAM temporarily.

Run php artisan media-library:regenerate to rebuild all conversions from original files. Target specific models or collections using --only-missing flags to avoid unnecessary processing. This preserves originals while updating derived formats safely during configuration changes.

Local filesystem works for single-server deployments but S3-compatible object storage scales better for multi-instance setups. In my experience deploying Laravel apps on EC2, using S3 with CloudFront CDN reduces origin load significantly compared to serving optimized images directly from application servers.

Validate MIME types strictly using Laravel Form Requests before processing. Never trust client-provided filenames or extensions. Configure max file sizes in both PHP and Nginx/Apache. Store uploads outside public root when possible and serve through signed URLs or controlled routes to prevent direct path traversal attacks.

Check if source images exceed reasonable dimensions before upload. Spatie cannot reduce file size below information content thresholds. Implement client-side compression or server-side pre-validation rejecting oversized originals. Also verify EXIF orientation stripping is enabled since metadata bloats output files unnecessarily.

Intervention provides low-level manipulation primitives requiring manual pipeline construction. Spatie integrates directly with Eloquent models, handles collections, generates responsive sets, and manages regeneration workflows. For structured content management in Laravel applications, Spatie reduces boilerplate significantly despite slightly steeper initial learning curve.

Yes, attach media to existing models programmatically using addMediaFromUrl or addMediaFromString methods. Populate the media table incrementally rather than forcing full re-imports. This approach works well when retrofitting optimization into legacy Laravel applications where downtime windows are constrained.

Missing system dependencies cause silent failures. Verify libpng, libjpeg, and webp libraries exist via php -m | grep -i gd. Ensure storage/media directories have correct ownership matching PHP-FPM user. On Deployer 7 setups, confirm shared storage symlinks persist across releases to prevent orphaned conversion files.

Generate multiple width variants and output srcset attributes in Blade templates. Set explicit width and height attributes preventing layout shift. Use lazy loading for below-fold images. Serve WebP with JPEG fallbacks via picture elements. These practices directly improve LCP and CLS metrics Google evaluates for ranking signals.

Absolutely, but pre-generate all conversions during import rather than on-demand. Use eager loading with withMedia to prevent N+1 queries on listing pages. Cache rendered HTML fragments containing image tags. On WooCommerce-to-Laravel migrations I have handled, this pattern sustained thousands of concurrent product views without degradation.

Share this article

Quick Contact Options
Choose how you want to connect me: