tito10047/progressive-image-bundle

High-performance progressive image loading bundle for Symfony with Blurhash placeholders, zero CLS, and smart responsive strategies.

Maintainers

Package info

github.com/tito10047/progressive-image-bundle

Type:symfony-bundle

pkg:composer/tito10047/progressive-image-bundle

Transparency log

Statistics

Installs: 251

Dependents: 0

Suggesters: 0

Stars: 17

Open Issues: 1

2.0.0-rc1 2026-08-16 09:07 UTC

README

Build Status PHP-CS-Fixer PHPStan Latest Stable Version License PHP Version Symfony Version Symfony Style Coverage Status

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.0 tag.

πŸ“– 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.json for 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.