tito10047 / progressive-image-bundle
High-performance progressive image loading bundle for Symfony with Blurhash placeholders, zero CLS, and smart responsive strategies.
Package info
github.com/tito10047/progressive-image-bundle
Type:symfony-bundle
pkg:composer/tito10047/progressive-image-bundle
Requires
- php: >=8.3
- intervention/image: ^4.2
- kornrunner/blurhash: ^1.2
- league/flysystem: ^3.35
- symfony/asset: ^6.4|^7.4|^8.1
- symfony/framework-bundle: ^6.4|^7.4|^6.4|^7.4|^8.1
- symfony/lock: ^6.4|^7.4|^8.1
- symfony/process: ^6.4|^7.4|^8.1
- symfony/stimulus-bundle: ^2.31|^3.3
- symfony/translation: ^6.4|^7.4|^8.1
- symfony/twig-bundle: ^6.4|^7.4|^8.1
- symfony/ux-twig-component: ^2.31
- symfony/web-link: ^6.4|^7.4|^8.1
Requires (Dev)
- ext-imagick: *
- deptrac/deptrac: ^4.7
- friendsofphp/php-cs-fixer: ^3.92
- oneup/flysystem-bundle: ^4.14
- phpstan/phpstan: ^2.1
- phpstan/phpstan-symfony: ^2.0
- symfony/asset-mapper: ^6.4|^7.4|^8.1
- symfony/dotenv: ^6.4|^7.4|^8.1
- symfony/http-client: ^6.4|^7.4|^8.1
- symfony/messenger: ^6.4|^7.4|^8.1
- symfony/test-pack: ^1.2
- symfony/yaml: ^6.4|^7.4|^8.1
README
High-performance, Fully Responsive Images for Symfony.
This bundle handles everything you need for modern image management. From fully responsive images with Tailwind-like selectors, to blur placeholders, to content-addressed generation of every required size, on local disk or any cloud storage Flysystem supports.
π Production Ready: This bundle is successfully deployed and running in production on live websites such as mostka.sk and vsetkosada.sk.
β οΈ Version 2.0 is experimental. It's a from-scratch DDD rewrite of the Variant pipeline and hasn't been tagged as a stable release yet β expect breaking changes before a final
2.0.0tag.
π Full documentation: pgi.blue
β Requirements
- PHP 8.3 or newer β required by
intervention/image(^4.2), the image-processing library the Variant pipeline is built on. - Symfony 6.4, 7.4, or 8.1+ β see
composer.jsonfor the exact per-package constraints.
β¨ Key Features
- π¨ Blur & Error Placeholders: Users see a beautiful Blurhash placeholder while loading. If an image is not found, the bundle automatically displays a stylish error placeholder.
- πΌοΈ Responsive via
<picture>: Uses the modern<picture>element with multiple<source>tags for optimal browser selection and performance. - π± Tailwind-like Selectors: Define responsiveness naturally directly in your template using familiar breakpoints.
- πΌοΈ Retina Support: Automatically generates 1x, 2x (and more) versions for high-density displays.
- βοΈ Content-Addressed Variant Pipeline: Every generated size is identified by a deterministic hash of its source + spec β no coordination needed across workers, and generation runs synchronously, on
kernel.terminate, or async via Messenger, your choice. - π― Zero CLS (Cumulative Layout Shift): Automatically reserves space for the image, preventing content jumping during load.
- β‘ Smart Preload: Automatically injects
<link rel="preload">for critical images (hero images), significantly improving LCP scores. - π§© Built to Be Extended: Storage backend, image engine, post-processors, path resolution and URL generation are all swappable Symfony services.
π¨ Usage
Simply use the Twig component. The bundle takes care of everything β it automatically calculates the required image dimensions for each breakpoint, resizes the original, and generates all necessary variants on the fly.
{# Basic usage - everything is automatic #} <twig:pgi:Image src="{{ asset(images/hero.jpg) }}" alt="Beautiful landscape" /> {# With Tailwind-like selectors for perfect responsiveness #} <twig:pgi:Image src="{{ asset(images/hero.jpg) }}" sizes="sm:12 md:6@landscape lg:4@square" alt="Responsive image" />
π± Selector Examples (Breakpoint Assignment)
The bundle supports flexible size assignment based on breakpoints you know from Tailwind or Bootstrap. For each selector, it automatically calculates the final pixel dimensions based on the container width defined by your CSS framework (Bootstrap or Tailwind) and generates the corresponding image.
| Selector | Meaning | Resulting behavior |
|---|---|---|
6 |
6 grid columns on all breakpoints | Width: 50% of container, original aspect ratio |
md:6 |
6 grid columns from md breakpoint |
From md up: 50% of container, below md: full width |
lg:4@landscape |
4 columns from lg with 16:9 aspect ratio |
From lg up: 33.3% of container, cropped to 16:9 ratio |
xs:12@square |
12 columns on xs with 1:1 aspect ratio |
Full width container, cropped to 1:1 square |
xxl:[430x370] |
Explicit dimensions for a specific breakpoint | Exact size 430x370px on xxl and larger |
xl:[100%]@landscape |
100% container width with landscape aspect ratio | Full width container, cropped to 16:9 ratio |
lg:4@square|circle |
4 columns on lg with a custom modifier |
Applies the circle modifier's config (e.g. a filter set) |
What is a "container"? The bundle automatically detects your CSS framework (Bootstrap or Tailwind) and extracts the exact container widths for each breakpoint from its configuration. It then uses these values to calculate the precise pixel dimensions for your images.
Full syntax reference: The Twig Component β Sizes syntax.
π Advanced Features
Point of Interest (PoI) Cropping
Define a focal point as pixel coordinates in the original image (e.g., pointInterest="544x320") so the most important subject stays in frame regardless of the target aspect ratio. The bundle finds the largest region of the original that matches the target ratio, centres it on the focal point, and scales the result β never slicing at original resolution. β Cookbook: Point of Interest Cropping
Smart Upscaling Protection
The bundle never generates an image larger than the original. If you need 1200px but the original is only 1000px, the bundle uses the original and prevents blurring.
Stream-based Metadata
To retrieve dimensions and Blurhash, the bundle doesn't load the entire image into RAM (no 20MB files in memory). It uses PHP streams to read only the necessary header bytes.
Content-Addressed Variant Generation
Every generated file is identified by an HMAC hash of its source + processing spec β the same request from any request or worker produces the same file, with no
coordination needed beyond a short-lived lock. Choose when generation actually runs: inline in the request (sync), deferred to kernel.terminate, or asynchronously via
Symfony Messenger (async, the default). β Variant Pipeline β Overview
Custom Modifiers & Filters
Extend the selector logic with your own modifiers (e.g., lg:4|circle), or add your own crop/resize/watermark filters via filter_sets. You can implement custom logic
and even prioritize modifiers to override default behavior. β Cookbook: Custom Modifier
π¦ Installation
composer require tito10047/progressive-image-bundle
See Getting Started for routing, cache pool, and full setup steps.
βοΈ Optional Configuration
# config/packages/progressive_image.yaml progressive_image: responsive_strategy: grid: framework: tailwind # or bootstrap ratios: landscape: "16/9" portrait: "3/4" square: "1/1" image_cache_enabled: true retina: enabled: true multipliers: [1, 2] # Opt into the Variant pipeline: generates and stores real resized files. # Without this, only responsive attributes/URLs are computed. variant_store: storage: 'oneup_flysystem.variants_filesystem' # any League\Flysystem\FilesystemOperator generation: strategy: async # async (default) | sync | terminate
Every available option is documented in the Configuration Reference.
π License
MIT License. See LICENSE for more information.
π Documentation
π Full documentation site: pgi.blue
| Section | What's in it |
|---|---|
| Guide | Installation, full configuration reference, the Twig component, responsive grid & ratios, caching, architecture |
| Variant Pipeline | How images actually get generated: content-addressed variants, the three generation strategies, filters/formats/quality, storage |
| Cookbook | Step-by-step recipes for extending the bundle: custom storage, image engine, post-processors, path decorators, URL generators, modifiers, async workers, point-of-interest cropping, serving behind nginx |
| API Reference | Every interface, model, route and command, as a quick lookup table |
The docs source lives in docs/ and is built with VitePress β run npm install && npm run dev inside that directory to preview changes locally.
π Changelog
All notable changes to this project are documented in CHANGELOG.md.