Responsive Images
Srcset
Generate multiple width variants for responsive srcset:
$set = $image->crop(800, 600)->widths(800, 600, 400);
echo $set->render(['alt' => 'Photo', 'sizes' => '(min-width: 800px) 800px, 100vw']);
<img src="photo_800.jpg"
srcset="photo_800.jpg 800w, photo_600.jpg 600w, photo_400.jpg 400w"
sizes="(min-width: 800px) 800px, 100vw"
width="800" height="600"
alt="Photo" />
Width range
Generate widths from a range:
$set = $image->widthsBetween(400, 1200, step: 200);
// → widths: 1200, 1000, 800, 600, 400
Density-descriptor srcset with densities()
For DPR-aware delivery without media queries, use densities():
$image = $factory->image('photo.jpg')->width(400);
$set = $image->densities(1, 2);
echo $set->render(['alt' => 'Photo']);
// <img srcset="photo.jpg?w=800 2x, photo.jpg?w=400 1x" width="400" alt="Photo" />
The browser picks the right asset based on devicePixelRatio alone — no sizes attribute needed.
Inside <picture> for art-directed density variants:
{{ image|densities(1, 2)|formats('image/webp', 'image/avif')|render }}
Pair with widths() for layout-aware delivery; use densities() when the asset’s display size is fixed and you only care about pixel density.
Format fallback
Add format variants (WebP + JPEG fallback):
$picture = $image->crop(800, 600)->formats(Format::Webp);
echo $picture->render(['alt' => 'Photo']);
<picture>
<source srcset="photo_800.webp" type="image/webp" />
<img src="photo_800.jpg" alt="Photo" />
</picture>
Combine with widths() for responsive + format:
$picture = $image->crop(800, 600)
->widths(800, 400)
->formats(Format::Webp);
Art direction
Serve different crops for different viewports using Picture:
use Timber\Chainsaw\Output\Picture;
$mobile = $factory->image('hero.jpg', $meta)->crop(400, 400);
$desktop = $factory->image('hero.jpg', $meta)->crop(1200, 400);
$picture = Picture::fallback($mobile)
->at('(min-width: 768px)', $desktop)
->formats(Format::Webp);
echo $picture->render(['alt' => 'Hero']);
Fluent builder
Build complex responsive pictures with the fluent API:
$picture = $factory->image('hero.jpg', $meta)
->crop(400, 400) // mobile fallback
->at('(min-width: 768px)') // tablet breakpoint
->widths(960, 640)
->at('(min-width: 1024px)') // desktop breakpoint
->widths(1920, 1440, 1200)
->formats(Format::Webp);
echo $picture->render(['alt' => 'Hero']);
Declarative (for Twig)
The picture filter accepts arrays for sources and formats:
$picture = $image->picture(
sources: [
'(min-width: 1024px)' => ['width' => 1200],
'(min-width: 768px)' => ['width' => 800],
],
formats: ['webp'],
);
Data URIs
Inline images as base64 (useful for small placeholders):
$dataUri = $image->crop(100, 100)->toDataUri();
// → data:image/jpeg;base64,...
Combine with blurHash() for tiny inline placeholders:
$placeholder = $image->blurHash()->toDataUri();
HTML output
Image, WidthSet / DensitySet, and Picture all expose ->render(array $attrs): MarkupInterface:
// Simple <img>
echo $image->render(['alt' => 'Photo', 'loading' => 'lazy', 'class' => 'hero']);
// Srcset <img>
echo $set->render(['alt' => 'Photo', 'sizes' => '100vw']);
// <picture>
echo $picture->render(['alt' => 'Photo']);
Attributes pass through to the HTML output. width and height are set automatically when dimensions are known. Stringifying an Image (e.g. <img src="{{ $image }}">) still returns the URL — render() is the explicit HTML path.
To customize the markup (lazy load, web components, <figure> wrappers, JSON output, etc.) supply your own renderer to ImageFactory — see Rendering.