just/thumbnailbundle
Just ThumbnailBundle integrates thumbnail generation into Symfony apps, providing configurable image filters, resizing, and caching for serving optimized previews from templates or controllers. It simplifies creating and managing thumbnail variants with minimal setup.
Reality check first: just/thumbnailbundle is a Symfony 2/3/4 bundle, not a Laravel package. There is no service provider, no facade, no Blade integration, and no config/thumbnailbundle.php. Composer will install it into a Laravel app (it's just PHP), but nothing will auto-register — you'd be wiring Symfony's DI container and Twig by hand. Treat it as a reference implementation to mine, not a dependency to require.
If you're evaluating it for a Symfony project (or porting ideas to Laravel):
composer require just/thumbnailbundle — then check composer.json for symfony/* version constraints before anything else. A bundle last tagged in 2025 but targeting Symfony 2/3/4 is the classic "maintained tag, stale internals" smell. Run composer why-not symfony/framework-bundle ^6.0 to see what blocks it.bundles.php on modern Symfony, AppKernel on legacy) and publish its config — expect a key like just_thumbnail with cache path, image driver (GD/Imagick), quality, and default dimensions.Resources/config/services.yml (or services.xml) and the bundle's routing file. That's where the actual value is — the route that serves /thumb/{path}/{width}/{height} and the service that wraps the image driver.First use case to validate: in a Twig template, something like {{ asset('images/hero.jpg')|thumbnail(300, 200) }} should emit a URL to the bundle's generation route, which lazily writes a cached file on first request and serves it thereafter. If that round-trip works, the bundle is functional; if the filter is missing or the route 404s, stop there.
For a Laravel app, the fastest path is to read that controller + service pair (roughly 100–200 lines) and reimplement it with intervention/image and a Route::get('/thumbs/{path}') — you'll spend less time than fighting the framework mismatch.
Symfony pattern the bundle encodes (copy this shape):
cache/thumbs/{hash}.jpg exists → if yes, X-Sendfile/readfile; if no, generate, write, serve. The URL is the cache key.|thumbnail(w, h) and get a URL. This is the single best idea in the bundle and it translates directly to a Blade directive: @thumb($path, 300, 200).Laravel translation, day-to-day:
// routes/web.php — mirror the bundle's route shape
Route::get('/thumbs/{size}/{path}', ThumbController::class)
->where('size', '\d+x\d+')
->where('path', '.*');
// controller: cache-first, generate-on-miss
$key = "thumbs/{$size}/".md5($path).'.jpg';
if (! Storage::disk('public')->exists($key)) {
Image::make(Storage::disk('public')->path($path))
->fit(...explode('x', $size))
->save(Storage::disk('public')->path($key), 80);
}
return Storage::disk('public')->response($key, headers: ['Cache-Control' => 'public, max-age=31536000, immutable']);
Integration tips that carry over from the bundle's design:
srcset from the same URL structure — 150x150, 300x300, 600x600 — and let the browser pick. The bundle's "size in the URL" model makes this trivial.Http::get() loop.auth — you want CDN-cacheable, unauthenticated responses.{path} segment is an LFI and disk-fill vector. The bundle was written in the Symfony 2 era, when this was routinely overlooked. If you port the code, resolve the path with realpath() and assert it starts with your configured media root. Never pass user input straight to Image::make().?w=50000&h=50000 on a 12MP source will OOM PHP-FPM. Enforce a max width/height and a whitelist of allowed sizes; return 404 for anything else.path + size, changing the source image leaves the old thumb cached forever with immutable headers. The bundle's approach typically misses this — add a source mtime or content hash into the URL (/thumbs/300x200/{hash}/photo.jpg) so new content gets a new URL.www-data? (3) is the driver extension (gd/imagick) actually installed? Silent blank images are almost always #3.composer.json constraints, each()/create_function, and dynamic properties are the usual breakers in Symfony-2-era code. Run the test suite against your target PHP version, or just read the source — at this size it's a 20-minute read.ImageManager binding, config('thumbs.path'), and a Blade directive.intervention/image + custom route above is roughly an afternoon of work with a package that has actual community support.How can I help you explore Laravel packages today?