Search by

silarhi / picasso-bundle

guillaume-sainthillier

Responsive images for Symfony: one Twig component for AVIF/WebP srcset, blur placeholders, Glide/Imgix, inspired by next/image

Package info

github.com/silarhi/picasso-bundle

Homepage

Documentation

Type:symfony-bundle

pkg:composer/silarhi/picasso-bundle

Statistics

Installs: 38 510

Dependents: 0

Suggesters: 0

Stars: 8

Open Issues: 2

v2.7.0 2026-10-29 21:09 UTC

README

Latest Stable Version Total Downloads License PHP Version CI Status Coverage

PicassoBundle

The missing image component for Symfony.
Inspired by Next.js Image — built for the Symfony ecosystem.

Write one line of Twig. Get AVIF, WebP, responsive srcset, blur placeholders, and lazy loading — automatically.

📖 Documentation

Before PicassoBundle

<!-- You write all of this manually... and maintain it forever -->
<picture>
    <source
        type="image/avif"
        srcset="/images/hero-640.avif 640w, /images/hero-1080.avif 1080w, /images/hero-1920.avif 1920w"
        sizes="100vw"
    />
    <source
        type="image/webp"
        srcset="/images/hero-640.webp 640w, /images/hero-1080.webp 1080w, /images/hero-1920.webp 1920w"
        sizes="100vw"
    />
    <img
        src="/images/hero-1080.jpg"
        srcset="/images/hero-640.jpg 640w, /images/hero-1080.jpg 1080w, /images/hero-1920.jpg 1920w"
        sizes="100vw"
        width="1920"
        height="1080"
        loading="lazy"
        alt="Hero"
    />
</picture>

After PicassoBundle

<twig:Picasso:Image src="hero.jpg" width="1920" height="1080" sizes="100vw" alt="Hero" />

Same output. Zero boilerplate. All formats, srcsets, and placeholders generated automatically.

Why PicassoBundle?

Images account for the largest share of page weight on most websites. Serving them correctly — with modern formats, responsive srcsets, proper lazy loading, and blur placeholders — is critical for both Core Web Vitals and user experience, but the implementation is tedious and error-prone.

PicassoBundle solves this the same way Next.js Image did for React: a single component that handles everything.

Without PicassoBundle With PicassoBundle
Format negotiation Manual AVIF/WebP/JPEG <source> tags Automatic from config
Responsive srcset Hand-crafted per breakpoint Generated from sizes prop
Blur placeholders DIY or skip it Built-in (LQIP, BlurHash, or custom)
Dimension detection Hardcoded or forgotten Auto-detected from image stream
LCP optimization Manually set loading/fetchpriority One priority prop
Image sources Filesystem only Filesystem, S3, Flysystem, Vich, URL, AssetMapper
CDN support Build your own integration Imgix out of the box, or plug in any CDN

Features

  • One component, full optimization — <twig:Picasso:Image> renders a complete <picture> with AVIF, WebP, and JPEG sources
  • Automatic responsive srcset — generates width descriptors for all configured breakpoints, no manual work
  • Blur placeholders — built-in LQIP and BlurHash support for instant perceived loading
  • Smart dimension detection — reads image dimensions from the stream automatically, preserves aspect ratio
  • Priority images — one prop for loading="eager" + fetchpriority="high" (LCP optimization)
  • Multiple image sources — Local filesystem, Flysystem (S3, GCS, Azure), VichUploaderBundle, remote URLs, AssetMapper assets
  • Local or CDN transforms — Glide for self-hosted, Imgix for CDN, or bring your own
  • Signed URLs — HMAC-signed transformation URLs prevent abuse
  • Private images — serve thumbnails from your own routes, behind your security voters, with the same srcset and formats
  • PSR-6 metadata caching — dimension detection and BlurHash results cached
  • Fully extensible — add custom loaders, transformers, or placeholders with PHP attributes (#[AsImageLoader], #[AsImageTransformer], #[AsPlaceholder])

Requirements

Glide, BlurHash, Flysystem, VichUploaderBundle, AssetMapper and remote URLs each need an optional package: see Installation.

Installation

composer require silarhi/picasso-bundle

# the <twig:Picasso:Image> component
composer require symfony/ux-twig-component

# and a transformer: Glide renders the images locally (Imgix needs no extra package)
composer require league/glide

Upgrading from 1.x? Follow UPGRADE-2.0.md, or the upgrade guide.

Quick Start

1. Configure a loader, a transformer and a placeholder:

# config/packages/picasso.yaml
picasso:
    loaders:
        filesystem:
            path: '%kernel.project_dir%/public/uploads'
    transformers:
        glide:
            sign_key: '%env(PICASSO_SIGN_KEY)%'
    placeholders:
        blur: { type: transformer } # optional: blurred preview while the image loads

2. Import the routes (required for Glide local serving):

# config/routes/picasso.yaml
picasso:
    resource: '@PicassoBundle/config/routes.php'

3. Use the Twig component in your templates:

<twig:Picasso:Image
    src="photo.jpg"
    width="800"
    height="600"
    sizes="(max-width: 768px) 100vw, 800px"
    alt="A beautiful landscape"
/>

This renders a <picture> element with <source> tags for AVIF and WebP, a fallback <img> with JPEG srcset, and an inline blur placeholder — all automatically. Placeholders are opt-in: without the placeholders entry, images render without one (see Placeholders).

Documentation

Everything else is on picasso-bundle.silarhi.dev:

Contributing

See CONTRIBUTING.md. Release notes are in CHANGELOG.md.

License

MIT License. See LICENSE for details.

Built with care by SILARHI.
If PicassoBundle saves you time, consider giving it a star on GitHub.