Modern websites need multiple image sizes for performance and responsiveness: small thumbnails for listings, medium images for article pages, and large versions for lightboxes. Manually resizing images is repetitive and error-prone. Intervention Image lets you programmatically read, resize, crop and save images in a few lines of code.
Quick links
-
Official docs: https://image.intervention.io/v3
Installation (fast)
- Install the package with Composer:
composer require intervention/image
- Make sure you have a PHP imaging driver installed — GD or Imagick. Example for Ubuntu + PHP 8.1 (adjust for your PHP version):
sudo apt-get install php8.1-gd
- If you serve files from storage/app/public, ensure the storage symlink exists:
php artisan storage:link
The problem we solve (concise)
Problem: users upload a single original image, but the site needs several pre-sized versions (e.g. 240x240, 800x600) for different UI components. Creating and naming those variants consistently, storing them, and keeping metadata in your DB is tedious and often done incorrectly.
Solution: on upload, store the original file, create resized versions using Intervention, save those files to your storage disk, and record the original file's metadata in the database.
Example: step‑by‑step controller flow
Below is a practical example that:
-
validates and stores the original image,
-
creates resized versions from an ImageSizes enum,
-
saves those resized files to storage/app/public/... in a predictable naming pattern.
Notes:
-
Replace Media and ImageSizes with your actual model/enum.
-
This uses the public storage disk. Adjust if you use S3 or another disk.
use Intervention\Image\ImageManager; use Illuminate\Support\Str; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Auth; use App\Models\Media; use App\Enums\ImageSizes; // example enum returning sizes like ['240x240','800x600'] use Illuminate\Http\Request; public function upload(Request $request) { // 1. validate $request-validate([ 'file' = 'required|file|image|max:5120' // 5MB max (adjust as needed) ]); // 2. basic file details $file = $request-file('file'); $originalName = $file-getClientOriginalName(); $extension = $file-getClientOriginalExtension(); $fileSizeKb = round($file-getSize() / 1024, 2); $mime = $file-getMimeType(); // 3. store original (public disk) $folder = 'uploads/images'; $path = $file-store($folder, 'public'); // e.g. uploads/images/abc123.jpg // 4. persist metadata (example) $media = Media::create([ 'name' = $originalName, 'url' = $path, 'type' = $mime, 'extension' = $extension, 'size' = $fileSizeKb, 'user_id' = Auth::id(), ]); // 5. if it's an image, create resized variants if (Str::startsWith($media-type, 'image')) { // Use GD driver or Imagick depending on your server $manager = new ImageManager(['driver' = 'gd']); // make() from the uploaded file path to preserve EXIF orientation $image = $manager-make($file-getRealPath())-orientate(); foreach (ImageSizes::map() as $size) { [$width, $height] = explode('x', $size); $resized = $image-resize((int)$width, (int)$height, function ($constraint) { $constraint-aspectRatio(); // keep aspect ratio $constraint-upsize(); // prevent upscaling }); $filenameWithoutExt = pathinfo($media-name, PATHINFO_FILENAME); $newFilename = "{$filenameWithoutExt}-{$size}.{$extension}"; // e.g. image-240x240.jpg $folderPath = dirname($media-url); // keep same folder as original // ensure the directory exists on the public disk Storage::disk('public')-makeDirectory($folderPath); // save encoded image bytes to the public disk Storage::disk('public')-put("{$folderPath}/{$newFilename}", (string) $resized-encode()); } } return response()-json(['media' = $media], 201); }
Example ImageSizes enum (PHP 8.1+)
Here's a tiny enum helper you can use. Replace the cases with whatever sizes you need.
?php enum ImageSizes: string { case THUMB = '240x240'; case MEDIUM = '800x600'; case LARGE = '1200x900'; public static function map(): array { return array_map(fn($c) = $c-value, self::cases()); } }
Tips & best practices
-
Use orientate() to fix rotation from mobile photos (EXIF data).
-
Decide on resize() vs fit(): resize() keeps aspect ratio and fits within the box; fit() crops to the exact dimensions.
-
Prevent upscaling with $constraint-upsize() so small originals don't get blown up.
-
Offload heavy processing to a queued job if uploads are large or you expect high volume.
-
Use the right storage disk: use Storage::disk('s3') if you want variants uploaded directly to S3.
-
Generate srcset on the frontend so browsers choose the best size for each device.
-
Validate input strictly (MIME type, max size) to avoid malicious uploads.
-
Keep filenames predictable (e.g. name-240x240.jpg) so you can programmatically reference variants.
Final notes
This approach gives you a simple, deterministic way to keep originals and multiple resized variants for responsive design and performance. If you want, I can:
-
convert this example into a queued Laravel Job,
-
show a Blade helper that prints srcset attributes, or
-
write a short post about using Intervention with S3.
Comments: