Search by

silarhi / picasso-bundle

guillaume-sainthillier

Responsive image component for Symfony, inspired by next/image

Package info

github.com/silarhi/picasso-bundle

Type:symfony-bundle

pkg:composer/silarhi/picasso-bundle

Statistics

Installs: 34 903

Dependents: 0

Suggesters: 0

Stars: 7

Open Issues: 2

v2.5.0 2026-10-12 10:52 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
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
  • 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 and remote URLs each need an optional package: see Installation.

Installation

composer require silarhi/picasso-bundle

# 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.