illuminate/image
Illuminate Image is a lightweight Laravel package offering a clean, fluent API for loading, resizing, cropping, converting, and caching images. It supports GD and Imagick drivers and integrates smoothly with Blade views, storage disks, and Laravel's filesystem. Simple image handling for Laravel apps...
illuminate/image is a small, community-maintained package (35 stars, MIT) from the illuminate GitHub org — not an official first-party Laravel package. Treat it as a thin, opinionated convenience layer rather than a framework component.
Minimal steps:
composer require illuminate/image
composer.json first. Confirm the illuminate/support and illuminate/filesystem version constraints match your Laravel version, and note whether it pulls intervention/image, gd, or imagick as the underlying driver. This single file tells you more than the README about whether it will fit your app.src/ for a facade or a Image::make()-style entry point.php artisan vendor:publish --tag=image-config) to set the default driver, quality, and disk. If there's no config file, hardcode as little as possible in your own config/images.php.gd or imagick is actually installed in your container/CI, not just locally.First use case: a thumbnail pipeline behind an upload form.
// app/Http/Controllers/AvatarController.php
public function store(StoreAvatarRequest $request)
{
$path = $request->file('avatar')->store('avatars/original', 'public');
Image::make(Storage::disk('public')->path($path))
->fit(256, 256)
->save(Storage::disk('public')->path("avatars/thumb/{$basename}.jpg"));
return back()->with('status', 'Uploaded');
}
Start there — one controller, one disk, one size — then generalize once you know how the package behaves with your files.
Wrap it in a dedicated service. Because the package is small and could be abandoned or swapped, isolate calls behind your own class. If you later move to Intervention Image, Spatie's image-optimizer, or Glide, you change one file.
// app/Services/ImageProcessor.php
class ImageProcessor
{
public function thumbnail(string $absolutePath, int $size): string
{
$out = $this->thumbPath($absolutePath, $size);
Image::make($absolutePath)->fit($size, $size)->save($out);
return $out;
}
}
Use it in queued jobs, not controllers, for anything >1 image. Image decoding is CPU- and memory-bound; a 12MP JPEG can cost 50–100MB of RAM. Dispatch a job after the upload is safely stored, and keep the original as the source of truth.
class GenerateThumbnails implements ShouldQueue
{
public function __construct(public string $path) {}
public function handle(ImageProcessor $processor): void
{
foreach ([128, 256, 512] as $size) {
$processor->thumbnail(Storage::disk('public')->path($this->path), $size);
}
}
}
Prefer absolute paths over URLs. Almost every image package wants a filesystem path, not a public URL. Use Storage::disk('x')->path($relative) and Storage::disk('x')->put() — never asset() — when persisting.
Expose variants via a DB column, not string building in Blade. Store the base path once and derive variants in a single accessor/helper so 128, 256, 512 stay consistent between the job and the view. This prevents the classic "thumbnail exists in dev but not prod because the size constant drifted" bug.
Test with Storage::fake(). Since the package writes to real files, fakes keep tests fast. For the image logic itself, commit a couple of tiny fixture files (a 2×2 PNG, an EXIF-rotated JPEG) rather than generating images in the test.
Integration tip — form requests do validation, not the package. MIME checks (image, mimes:jpg,png,webp), file size, and dimension limits belong in StoreAvatarRequest. Assume the package does not sandbox inputs.
composer.json, read the source before relying on it, and keep your abstraction layer thin. There is no LTS guarantee, no security team, no deprecation policy. Budget for replacement.gd and imagick handle transparency, color profiles, and EXIF orientation differently. Confirm the same extension is installed in Docker/CI/Forge. A common symptom is WebP uploads failing only in production.->orientate() / ->autoOrient() automatically — many thin wrappers don't. If not, add it explicitly.Allowed memory size exhausted. Raise memory_limit in the queue worker, or check dimensions (getimagesize) and reject oversized uploads before decoding.env() inside it, php artisan config:cache will null those values in production. Move env reads into config/*.php of your own if needed.image/svg+xml, you have an XSS/SSRF surface. Reject SVG at validation unless you have a sanitizer (e.g. enshrined/svg-sanitize).getimagesize(), not just file size.ImageProcessor wrapper, (2) Laravel macros if it exposes a fluent class, (3) replacing it. Don't invest in patching it.dd(get_class(Image::make($path)), getimagesize($path)) to confirm the driver and source dimensions before assuming the package is at fault.www-data but the queue is forge, you'll get Permission denied on .save(). Set the disk root permissions once and test the queue path in staging.How can I help you explore Laravel packages today?