spatie/image
Expressive PHP image manipulation with a fluent API. Resize, crop, rotate, apply filters (greyscale, brightness, sharpen), adjust quality, and auto-orient images. Load from a path and save in place or to a new file.
Installation:
composer require spatie/image
Ensure exif PHP extension is enabled (required since v1.5.3).
First Use Case: Resize an image while preserving aspect ratio:
use Spatie\Image\Image;
Image::load(public_path('original.jpg'))
->width(300)
->height(300)
->save(public_path('resized.jpg'));
Key Documentation:
Image Processing Pipeline:
// Chain operations for efficiency
Image::load($path)
->resize(800, 600, function ($constraint) {
$constraint->aspectRatio();
})
->watermark(public_path('watermark.png'), 0.2)
->save($outputPath);
Dynamic Thumbnail Generation:
// Generate multiple sizes in a single pass
$image = Image::load($originalPath);
$image->resize(100, 100)->save($thumbnailSmall);
$image->reset()->resize(300, 300)->save($thumbnailLarge);
Batch Processing:
// Process all images in a directory
foreach (Storage::disk('public')->files('uploads') as $file) {
$path = storage_path("app/{$file}");
Image::load($path)
->resize(1200, null, function ($constraint) {
$constraint->upsize();
})
->save($path);
}
Laravel Filesystem:
Use Storage::disk() for cloud storage (S3, etc.):
Image::load(Storage::disk('s3')->path('original.jpg'))
->resize(400, 400)
->save(Storage::disk('s3')->path('processed.jpg'));
Model Observers: Automate processing on upload:
// app/Observers/ImageObserver.php
public function saved(UploadedFile $file) {
Image::load($file->path())
->resize(800, 600)
->save($file->path());
}
Queue Jobs: Offload heavy processing:
// app/Jobs/ProcessImage.php
public function handle() {
Image::load($this->path)
->resize(1500, 1500)
->save($this->path);
}
Custom Drivers: Extend for specialized needs (e.g., PDF support):
// app/Drivers/PdfDriver.php
class PdfDriver extends \Spatie\Image\Support\Drivers\Driver {
public function __construct() {
$this->driverName = 'pdf';
}
// Implement required methods
}
Driver Selection:
libvips:
Image::load($path)->driver('vips')->resize(2000, 2000);
EXIF Orientation:
Image::load($path)->driver('imagick')->orientation();
File Extensions:
// Avoid: Image::load()->save('output'); // May lose format
Image::load()->save('output.webp'); // Force format
Quality Settings:
Image::load()->quality(85)->save(); // Balanced JPEG quality
Canvas Resizing:
resizeCanvas() vs resize():
// resizeCanvas: Expands canvas (adds padding)
// resize: Crops/stretches to fit
Driver-Specific Issues:
phpinfo() for installed drivers:
if (!extension_loaded('imagick')) {
throw new \RuntimeException('Imagick extension required');
}
Memory Limits:
Image::load()->driver('vips')->resize(4000, 4000);
Error Handling:
try {
Image::load($path)->resize(100, 100)->save();
} catch (\Spatie\Image\Exceptions\CouldNotLoadImage $e) {
Log::error("Invalid image: {$path}");
}
Batch Processing:
$images = collect([...]);
$images->each(fn($path) => Image::load($path)->resize(500, 500)->save($path));
Caching:
if (!file_exists($cachedPath)) {
Image::load($original)->resize(300, 300)->save($cachedPath);
}
Lazy Loading:
save():
$image = Image::load($path)->resize(200, 200); // No processing yet
$image->save(); // Triggers processing
Custom Filters:
Filter class for reusable operations:
class CustomFilter extends \Spatie\Image\Filters\Filter {
public function apply($image) {
$image->brightness(10)->contrast(20);
return $image;
}
}
Event Hooks:
Image::load($path)->on('post-resize', function () {
Log::info('Image resized successfully');
});
Format-Specific Logic:
save() behavior per format:
Image::load()->save(function ($image, $path) {
if (str_ends_with($path, '.webp')) {
$image->quality(70);
}
$image->save($path);
});
How can I help you explore Laravel packages today?