How-to library · Developer docs

How-to library

Task-shaped pages: a title that starts with a verb, the situation in a few lines, the steps, the working code in both engines, and why this way rather than another. Found from a search, done, and left.

How-to Images

Serve WebP without writing a picture tag yourself

Turn on Generate picture tag and every getHtml() renders a picture element with a WebP source and fallbacks, no template change. Per image, toOptimizedFormat() converts to WebP; browsers that cannot show it get the original.

Context

Context. Two routes to WebP, and they combine. The site setting Generate picture tag turns every image rendered through getHtml(), and every image in rich text, into a <picture> with a WebP source first, a source in the original format, and an <img> fallback; the browser picks the first it can show. Per image, toOptimizedFormat() converts to WebP in the template. WebP always goes through the Cloudinary service, and if no service can convert when the file is fetched, the original is delivered under the WebP URL, which browsers accept.

Steps

  1. Turn on Generate picture tag in the image settings. Leave Offer hq image on for 2x sources.
  2. Change nothing in templates that already call getHtml(): they now render picture elements.
  3. Where you emit a plain URL instead (a CSS background, a data attribute), convert in the template and use the WebP path, with the original as a fallback where the consumer cannot negotiate.
  4. Keep the order resize, convert, render everywhere, so URLs are consistent and the cache is not doubled.

Template code

Template code
{* renders a picture element with the setting on, a plain img with it off *}
{$image->getResized(1200, 675, 'fit')->getHtml(['alt' => $image->getName()|escape, 'loading' => 'lazy'])}

{* a URL for CSS or a script: convert explicitly *}
{$hero = $image->getResized(1600, 600, 'fit')}
<div class="hero" style="background-image: url('{$hero->toOptimizedFormat()->getPath()}')"></div>
{{ image.getResized(1200, 675, 'fit').getHtml({'alt': image.getName()|e, 'loading': 'lazy'}) }}

{% set hero = image.getResized(1600, 600, 'fit') %}
<div class="hero" style="background-image: url('{{ hero.toOptimizedFormat().path }}')"></div>

Why this and not that

Not a converted <img src="….webp"> without a fallback when the setting is off: a browser that cannot show WebP gets the original bytes anyway under that URL, so it works, but you have thrown away the negotiation the picture element gives you for free. Not converting SVGs: they are never converted and render as plain <img>. Judge the compression on your own images with the URL parameters (?l=1&q=80 against ?l=0) before touching the quality setting.

Go deeper

Was this page helpful?

Your answer goes to the docs team.