folklore/laravel-image
A Laravel image package that simplifies common image workflows with a clean API for resizing, cropping, and generating thumbnails while integrating with Laravel’s filesystem and configuration. Useful for handling uploaded images and model attachments in apps.
Architecture fit. The 2.x line is a re-architecture of the package around explicit contracts rather than a bag of helpers, and that is the most important fact for planning. The public surface is now: an immutable PendingImage builder (Image::for($ref)->preset(...)->signed(...)->url()), a config file (config/image.php) that owns presets, versions, signing keys, hosts, limits and delivery policy, and a set of named contracts — ImageSpec, UrlCodec, Signer, SourceResolver/ImageSource, Renderer/Operation, DerivativeStore, ImageVariant. Resolvers and renderers are registered from config or at runtime, and the route layer (Route::image($prefix) / Image::routes()) is opt-in rather than auto-registered. For a Laravel application this is a clean fit: everything is configuration- and container-driven, nothing requires a service provider fork, and the URL-signing and rendering paths can be swapped independently. The trade-off is that it is no longer a drop-in "resize this file" utility — adopting 2.x is a small platform decision about where image URLs are produced and where derivatives live.
Integration feasibility. Three hard gates exist before any code is written:
media resolver of laravel-mediatheque 2, which has not shipped yet — the release notes say it "ships in its next alpha" and instruct ^2.0@alpha. This means the package cannot be installed and exercised end-to-end on its own today unless you only use the disk resolver. That is a real sequencing constraint, not a footnote.intervention/image >= 4.3.4) via InterventionRenderer, with an auto driver preferring libvips, then Imagick, then GD. Production image quality/performance therefore depends on what is installed on the host — libvips in particular requires FFI and a system library.The disk resolver alone (DiskResolver, images on any Laravel disk under an optional root, path containment, version from mtime or S3 ETag) is independently useful and is the lowest-risk way to get hands-on with the alpha.
Technical risk.
2.0.0-alpha.1. The 1.x code is removed outright, the 1.x API only returns as a compatibility layer in the beta (decision 0007), and only 1.x URLs are decoded today. Anything calling the 1.x PHP API breaks immediately with no shim.image.derivatives.lock), a derivatives disk, a signing key (derived from APP_KEY/APP_PREVIOUS_KEYS), and, for libvips, FFI and a system library. Each of these is a new failure surface.strict mode an unsigned spec gets a signed URL and presets are served only at their current version and widths. Getting strict wrong in production is a 403 incident, not a soft failure.laravel-mediatheque 2 is also pre-release, so you would be pinning two moving packages simultaneously.v2.x commit) and the CI matrix (PHP 8.3–8.5 × Laravel 12–13 × GD/Imagick/libvips, Larastan at max level) are strong signals of engineering discipline, but this reads as a small-team or single-maintainer project. Treat upstream responsiveness as a risk to monitor, not a given.Key questions to put to the maintainers (and to your own platform team).
laravel-mediatheque 2 alpha land, and is the ^2.0@alpha constraint stable for the duration of our pilot?APP_PREVIOUS_KEYS semantics, overlap window)?image:doctor's session-cookie check flag it as a problem we must fix?presets versions — can a preset's widths be changed without invalidating cached derivatives?Stack fit. A good fit only for applications already, or imminently, on PHP 8.3+ and Laravel 12+. For those, the package is idiomatic: config-driven, container-resolved, no global state, an opt-in route, and a testing story (Image::fake() with an in-memory derivative store, plus published contract test traits for SourceResolver, Renderer, DerivativeStore, ImageVariant). The contract test traits are unusually valuable here — they let us validate our custom resolver or store against the package's own expectations rather than against documentation.
Migration path. Plan it in three stages rather than as a single upgrade:
Image::make, ->resize, ->url, any Imagine references), JS-side URL construction, and existing derivative directories. The 1.x PHP API has no replacement in alpha 1 — this inventory defines the size of the blocker. Confirm PHP/Laravel versions for every app.disk resolver and DiskStore, with the route opt-in and strict on. Run php artisan image:doctor in CI as a hard gate — it checks the driver and its formats, FFI/libvips, the disks (--write performs a real write/read/delete), the lock store, the signing key, the route and its session-cookie behaviour, and the URL host. This is the single most valuable operational asset in the release and should be treated as a required pipeline step, not a debugging convenience.UrlCodec as promised. Then flip presets to their v2 versions and let versioned URLs carry the immutable cache headers.Compatibility notes. 1.x URLs continue to be decoded — so a mixed fleet (some pages emitting v1 URLs, some emitting v2) will work during rollout. That is the key property that makes a gradual migration possible and should be verified explicitly rather than assumed. Publish config/image.php and change only the keys you need; the config is designed for partial overrides.
Sequencing (dependency-ordered).
APP_KEY/APP_PREVIOUS_KEYS are set and rotated sensibly.intervention/image >= 4.3.4 and choose a driver (libvips where FFI is available and permitted by security review; Imagick as the pragmatic default; GD as fallback).image:doctor into CI and staging deploy gates.Image::for(...)->preset(...)->url(), route enabled, verified through the CDN.Maintenance. Alpha churn is the dominant cost. Expect breaking changes between alpha and beta and between beta and GA; pin exact prerelease versions and upgrade deliberately, reading decision records (0001, 0002, 0007, 0008, 0009 are the load-bearing ones) rather than changelogs. The dependency on laravel-mediatheque 2 means two packages must move in step — budget for that as a joint upgrade unit, not two independent ones. The removal of the JS package is a permanent maintenance shift: URL generation is now server-side only, so any future surface (mobile app, SPA, edge worker) needs a server endpoint or a signed-URL API rather than a client library. Long-term, the contract-based design should reduce maintenance — swapping a renderer or store is an implementation change, not a fork.
Support. Documentation is genuinely good for a pre-release: getting started, presets, URLs and signatures, and delivery-behind-a-CDN guides, including a concrete Cloudflare walkthrough. image:doctor converts most "images are broken" tickets into a self-service diagnostic. That said, support load will concentrate in three predictable areas: 403s from signature/expiry/strict-mode mistakes, 503s from render lock contention, and missing-format errors from a driver that lacks AVIF/WebP/HEIC support. Write runbooks for those three before go-live. Note also that Image::fake() and InMemoryDerivativeStore mean test failures won't mask real disk/lock/driver problems — integration coverage must exist separately.
Scaling. The design scales the way image pipelines should: render once per derivative behind an atomic lock, store on a disk, serve with Cache-Control: public, max-age=31536000, immutable for versioned URLs, private for private sources, plus ETag and 304s, and either stream or redirect to the store's URL (public disks). Put a CDN in front and origin load becomes near-zero for warm presets. Watch three cost centres: (a) derivative cardinality — presets × responsive widths × formats multiplies fast, and each one is a stored object to pay for and to invalidate; (b) cold-start stampedes — mitigated by the lock plus 503/Retry-After, but a new preset version is a fleet-wide cold render, so roll preset versions out deliberately; (c) render memory and CPU — the renderer temporarily raises image.memory_limit, and libvips/GD worker sizing should be set from observed p95, not guessed. DiskResolver caches the S3 ETag-derived version for version_ttl seconds, which bounds metadata lookups but is also your staleness window.
Failure modes to instrument explicitly.
Retry-After. Correct behaviour, but it will surface as user-visible latency or errors during cache-miss bursts. Alert on 503 rate, not just on 5xx in aggregate.APP_PREVIOUS_KEYS overlap covers the longest TTL in use plus CDN cache lifetime.image:doctor --write catches configuration errors at deploy time; runtime failures still need a metric.image:doctor. The image route should be stateless and cache-safe; if it inherits session middleware, CDN caching semantics break.Ramp-up. Assume one to two engineer-days to a working vertical slice for someone fluent in Laravel and Image 4, and materially more for the full migration, because the 1.x API gap blocks existing call sites until beta. Ramp-up aids are strong and should be used in order: the four guides, then image:doctor as an interactive teacher on a staging box, then the public contract test traits as executable documentation of what each extension point must do. The conceptual jumps to plan for are (a) URLs as the primary unit of work rather than images — you build a spec, sign it, hand out a URL; (b) versioning as the cache-invalidation primitive; (c) strict mode as a security posture that will initially feel like it is fighting you. Do not attempt a big-bang migration ahead of the compatibility layer; a phased, one-surface-at-a-time rollout with image:doctor as the gate is the only sequencing that matches the release's actual maturity.
How can I help you explore Laravel packages today?