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.