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.
<!-- 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><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.
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 |
- 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])
- PHP 8.2+
- Symfony 6.4, 7.x or 8.x
- Symfony UX Twig Component 2.13+
Glide, BlurHash, Flysystem, VichUploaderBundle and remote URLs each need an optional package: see Installation.
composer require silarhi/picasso-bundle
# and a transformer: Glide renders the images locally (Imgix needs no extra package)
composer require league/glideUpgrading from 1.x? Follow UPGRADE-2.0.md, or the upgrade guide.
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 loads2. 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).
Everything else is on picasso-bundle.silarhi.dev:
- Guides: responsive images, fit modes, placeholders, priority images, private images, cache purge, JSON API
- Image sources: filesystem, Flysystem, VichUploaderBundle, remote URLs, chain, custom
- Transformers: Glide, Imgix, serving from a CDN, custom
- Reference: configuration, component, Twig functions, routes
- Concepts: how it works, performance
See CONTRIBUTING.md. Release notes are in CHANGELOG.md.
MIT License. See LICENSE for details.
Built with care by SILARHI.
If PicassoBundle saves you time, consider giving it a star on GitHub.