Twig Integration

Setup

use Timber\Chainsaw\Output\MarkupInterface;
use Timber\Chainsaw\Twig\ChainsawExtension;
use Twig\Runtime\EscaperRuntime;

$twig->addExtension(new ChainsawExtension($factory));

// One-liner: mark the element family html-safe so an element passed as a plain
// variable ({{ el }}) prints its markup without |raw. The |img and |picture
// filters already carry is_safe, so the common {{ image|img(alt: '…') }} case
// needs nothing; this covers pass-an-element-through-a-variable.
$twig->getRuntime(EscaperRuntime::class)
    ->addSafeClass(MarkupInterface::class, ['html']);

The extension accepts either an ImageFactory or a FactoryRegistry.
Pass a registry to expose named factories to img() (below); a bare factory is
treated as the registry’s single default.

Function

img(source, name?, meta?)

Create an Image from a path string. The optional second argument selects a named
factory from the FactoryRegistry
mirroring Symfony’s asset(path, packageName) — and falls back to the default
factory when omitted or null:

{% set image = img('photo.jpg') %}                {# default factory #}
{% set image = img('photo.jpg', 'cloudflare') %}  {# named factory #}
{% set image = img('photo.jpg', meta: meta) %}    {# explicit source metadata #}

An unregistered name throws FactoryNotFound. With a single ImageFactory (no named
factories), only the default form applies.

Pass metadata with the named argument (meta: meta) rather than positionally — the
second positional argument is the factory name, not the metadata. The Twig function
img() (which creates an Image) and the Twig filter |img (which renders one)
are separate registries; position disambiguates.

Filters

Manipulation filters

All filters accept an Image, a srcset set (WidthSet / DensitySet), or a string
(auto-coerced to Image). Arguments can be passed positionally or by name — the names
below are the filter’s named-argument vocabulary, identical to the fluent PHP API:

{{ 'photo.jpg' | width(400) | img(alt: 'Photo') }}
{{ 'photo.jpg' | crop(800, 600) | greyscale | quality(80) | img(alt: 'Photo') }}
{{ 'photo.jpg' | cover(width=800, height=600, anchor='smart') | img(alt: 'Photo') }}
{{ 'photo.jpg' | cover(800, 600, noUpscale=true) | img(alt: 'Photo') }}

The table below is machine-derived from Image’s #[Dispatchable] methods
(composer twig:generate); ? marks optional arguments.

Filter Arguments Returns
scale factor Image
width width Image
height height Image
contain width, height, noUpscale? Image
stretch width, height Image
pad width, height, background?, noUpscale? Image
cover width, height, anchor?, noUpscale? Image
crop width, height, anchor?, noUpscale? Image (alias of cover)
manualCrop width, height, x, y Image
cropToRatio ratio, anchor? Image
padToRatio ratio, background? Image
autocrop tolerance?, color? Image (Image::trim())
blur radius Image
sharpen amount Image
brightness amount Image
contrast amount Image
gamma gamma Image
pixelate size Image
greyscale Image
sepia Image
saturation amount Image
hue angle Image
negate Image
dither algorithm? Image
blurHash width?, height?, componentsX?, componentsY? Image
thumbHash width?, height? Image
flip direction Image
rotate degrees, background? Image
autoOrient Image
background color Image
border width, unit?, color?, type? Image
watermark source, position?, paddingX?, paddingY?, paddingUnit?, width?, height?, sizeUnit?, fit?, alpha? Image
tiledWatermark source, width?, height?, sizeUnit?, fit?, alpha?, gapX?, gapY?, gapUnit? Image
dpr dpr Image
to format Image (Image::format())
quality quality Image
preset name Image
widths ...widths WidthSet
densities ...ratios DensitySet

For the anchor arguments, pass a compass string ('center', 'top', 'bottom-left',
…), a detection keyword ('smart', 'entropy', 'attention', 'face'), or
'object:<class>'. For ratio, pass a string like '16:9'.

For to, pass the format string: 'webp', 'avif', 'jpg', 'png', 'gif'.

Every manipulation has a dedicated filter except without() — its argument is a
manipulator class-string, awkward in a template, so it stays on the manipulate hash
filter below ({{ image | manipulate({without: '...'}) }}).

manipulate

Apply operations from a hash:

{{ 'photo.jpg' | manipulate({ width: 400, blur: 10, quality: 80 }) | img(alt: 'Photo') }}

img (output filter)

The presentation portal: turn an Image or set into an <img>. Its arguments mirror
Image::img()alt (required), then layout, sizes, priority, loading,
decoding, fetchPriority, placeholder, inlineStyle, attrs. The filter is
marked safe, so it prints raw:

{# <img> tag #}
{{ image | img(alt: 'Photo') }}
{{ image | img(alt: 'Photo', priority: true, attrs: { class: 'hero' }) }}

{# a layout derives the srcset + sizes #}
{{ image | img(alt: 'Photo', layout: 'constrained') }}

{# an explicit srcset #}
{{ image | widths(800, 400) | img(alt: 'Photo', sizes: '100vw') }}

{# a blur placeholder #}
{{ image | img(alt: 'Photo', placeholder: 'thumbhash') }}

See Layout & Sizes for layout / sizes, and
Placeholders for placeholder.

picture

Declarative responsive <picture>. alt comes first, then the media-keyed source
map and the format list; the remaining img() intent arguments follow. Also safe:

{{ 'hero.jpg' | picture(
    alt: 'Hero',
    sources: {
        md: { cover: [960, 640], widths: [960, 1920], sizes: '50vw' },
        max-md: { cover: [640, 640] },
    },
    formats: ['webp', 'avif'],
) }}

Each source entry is a manipulation op map built from the pristine root; the reserved
sizes key is lifted out before dispatch, and an unknown op key throws. Media keys
accept a screens name (md, max-md), a CSS
media type, or a raw query. See Art direction.

Full example

{# Responsive image — layout derives the srcset and sizes #}
{{ img('hero.jpg') | cover(1200, 600) | img(alt: 'Hero banner', layout: 'constrained', priority: true) }}

{# Explicit widths + sizes #}
{{ img('hero.jpg') | cover(1200, 600) | widths(1200, 800, 400) | img(
    alt: 'Hero banner',
    sizes: '(min-width: 1200px) 1200px, 100vw',
) }}

{# Thumbnail preset #}
{{ img('avatar.jpg') | preset('thumbnail') | img(alt: user.name) }}

{# Greyscale with WebP <picture> #}
{{ img('photo.jpg') | crop(600, 400) | greyscale | quality(80) | picture(alt: 'Photo', formats: ['webp']) }}

The filters dispatch through the renderers configured on the ImageFactory (defaults:
HtmlImgRenderer, HtmlSourceRenderer, HtmlPictureRenderer). To customize the
markup, supply your own renderer to the factory — see Rendering.