Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Ux Image Laravel Package

symfony/ux-image

Symfony UX Image offers a Twig component and PHP helpers for rendering and manipulating images in Symfony applications, simplifying responsive output, lazy loading, and integration with image processing or optimization backends.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is symfony/ux-image. Despite the symfony/ namespace, nothing in the bridge layer requires the Symfony framework — a Laravel app can use it as a pure URL-generation library for image CDNs. As of v3.6.0 it ships four bridges in the box: Cloudflare, KeyCDN (with Secure Token signing), imgix, and Cloudinary.

  1. composer require symfony/ux-image. Pull it in alongside whatever you already use for uploads (intervention/image, Spatie Media Library, Flysystem). You do not need Symfony Flex, the recipe, or config/packages/ux_image.yaml.
  2. Look first at Symfony\UX\Image\Bridge\* — one class per CDN — and at the new preset support (Symfony\UX\Image\Preset\* or the config key the bridge accepts for named transforms). Those two things are 90% of what you'll touch. The Twig ux_picture component and ImageInterface/Picture classes are the Symfony-facing front end and are the part you'll most likely reimplement in Blade.
  3. Bind your bridge as a singleton in a service provider, reading credentials from config/services.php + .env (zone hash for Cloudflare, hostname + token for KeyCDN, cloud name + API key for Cloudinary, source/domain for imgix).
  4. First use case: take a relative storage key (products/abc.jpg), run it through the bridge with a named preset ('card' → 400×300, fit=crop, format=auto), and emit it in a Blade component as src plus a 2–3 entry srcset.

Implementation Patterns

Wrap the bridge, don't sprinkle it. Expose a single ImageUrl service with methods like url(string $path, string $preset, array $overrides = []) and srcset(string $path, array $presets). The bridge API is transform-option shaped and CDN-specific; your app should speak presets only. This is exactly what the new Add presets feature is for — define presets once in config/image.php, and swapping Cloudinary for imgix later becomes a config change plus one binding.

Preset-driven Blade component. Build a <x-image :path="$model->image" preset="hero" /> that reads the preset from config, calls the bridge, and renders srcset/sizes. Keep the bridge call in the component class, not the template, so you can cache and test it.

Resolve signed URLs at render time, not at upload time. With KeyCDN Secure Token (and Cloudinary's signed URLs), the token is time-bound. Store the asset key in the database, never the signed URL. If you use Cache::remember on rendered fragments, either exclude signed URLs from the cache or set the TTL below the token TTL.

Model the path, hide the domain. Persist products/abc.jpg, let the bridge prepend the CDN base. This keeps local dev (public disk), staging, and prod interchangeable and avoids stale absolute URLs in your database.

Layer it onto the upload pipeline. After your existing upload job writes the file, you don't need to generate derivatives — the CDN does that on first request. Use a queued "warmup" job that Http::get()s the 3–4 preset URLs for new hero images so the first visitor doesn't pay the transform cost.

Testing. Bind a trivial fake implementing your ImageUrl contract in TestCase::setUp() so feature tests assert on preset names, not on signed query strings. For unit tests of URL construction, instantiate the real bridge directly with dummy credentials — they're pure string builders and need no network.

Gotchas and Tips

  • Symfony-first ergonomics. The Twig component and the ImageInterface/Picture machinery assume Twig plus symfony/asset for versioning. In Laravel you either install twig/twig and fight two view layers, or (recommended) ignore the component entirely and drive the bridges directly. Budget an hour to read the bridge source before committing.
  • Don't import Symfony config. The Flex recipe will want to write config/packages/ux_image.yaml if you run composer require with Flex present in a mixed repo. Keep credentials in config/services.php and a small config/image.php of your own.
  • Preset naming is yours, options are theirs. Presets reduce but don't eliminate CDN lock-in: fit, gravity, dpr, and format negotiation are spelled differently per provider. Keep overrides to a documented subset if you care about portability.
  • Signature fragility. Secure-token and Cloudinary signatures are computed over the path and the query string in a defined order. Re-ordering or appending utm_*-style params after signing invalidates the URL and you get a 403 with no useful body. Log the exact unsigned URL on failure and compare byte-for-byte.
  • Clock skew and TTL. KeyCDN token expiry is absolute; a token minted on a server a few minutes ahead will be rejected. Keep TTL ≥ 300s and make sure all app servers share NTP. Very short TTLs plus full-page caching is the classic "images 403 after 10 minutes" bug.
  • srcset + signed URLs + HTTP cache. Each preset gets a different token, so cache keys explode and edge hit-rate drops. Prefer path-based signing or long TTLs on presets, and short TTLs only on genuinely private assets.
  • Local development. Point the bridge at a passthrough or disable it behind a config flag (IMAGE_CDN=null) so php artisan serve doesn't require network access for every page render.
  • Extension point. If you're on Bunny, ImageKit, or a self-hosted Thumbor, the bridge interface is small enough to implement yourself — do that rather than abusing the Cloudflare bridge's query params.
  • Version pinning. This is a young package moving fast (whole package plus two bridges landed in one minor, presets in another). Pin with ^3.6 and read the bridge changelog on every minor; internal bridge constructors have already churned once.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings