Convert images to Webp and have fallback jpg/png

I would like to convert and display webp images – while having a fallback to other formats jpg/png for those browsers that don’t support modern image formats. I don’t want different dimensions (responsive images).

I’ve been trying to follow

But I don’t find it easy to understand. I’ve got this block snippet


<?php if ($image = $block->event_photo()->toFile()): ?>
	<picture>
		<source srcset="<?= $image->srcset('extra-images-webp') ?>" type="image/webp">
		<img
			alt="<?= $image->alt() ?>"
			src="<?= $image->thumb(['width' => 780, 'height' => 520, 'crop' => 'center'])->url() ?>"
			width="780"
			height="520"
		>
	</picture>
<?php endif ?>

And this config.php file

<?php
return [
	'debug' => false,


	'thumbs' => [
		'srcsets' => [
			'hero-images' => [
				'780w'  => ['width' => 780, 'height' => 520, 'crop' => 'center'],
				'1200w'  => ['width' => 1200, 'height' => 800, 'crop' => 'center'],
				'1600w'  => ['width' => 1600, 'height' => 1066, 'crop' => 'center']
			],

			'hero-images-webp' => [
				'780w'  => ['width' => 780, 'height' => 520, 'crop' => 'center', 'format' => 'webp'],
				'1200w'  => ['width' => 1200, 'height' => 800, 'crop' => 'center', 'format' => 'webp'],
				'1600w'  => ['width' => 1600, 'height' => 1066, 'crop' => 'center', 'format' => 'webp']
			],

			'extra-images' => [
				'780w'  => ['width' => 780, 'height' => 520, 'crop' => 'center']
			],

			'extra-images-webp' => [
				'780w'  => ['width' => 780, 'height' => 520, 'crop' => 'center', 'format' => 'webp']
			],

			'news-images' => [
				'780w'  => ['width' => 780]
			],

			'news-images-webp' => [
				'780w'  => ['width' => 780, 'format' => 'webp']
			],

			// more srcsets as needed
		]
	]
];

Is this correct?

Rather than have to write this out in full in my block
src="<?= $image->thumb(['width' => 780, 'height' => 520, 'crop' => 'center'])->url() ?>"
Is it possible to link to the config file?

You can use <?= $image->thumb('my-preset-name')->url() ?>, where 'my-preset-name' maps to a configuration array.

Relevant docs for the config part:

So in your case it might look like:

<?php // site/config/config.php

return [
	'thumbs' => [
		'presets' => [
			'my-preset-name' => ['width' => 780, 'height' => 520, 'crop' => 'center']
		],
		'srcsets' => [
			/* your current srcsets config */
		],
	]
];

Hey thanks. I’m still struggling. This is my block code


	<?php if ($image = $block->event_photo()->toFile()): ?>
		<picture>
			<source srcset="<?= $image->srcset('extra-images-webp')->url() ?>" type="image/webp">
			<img 
				alt="<?= $image->alt() ?>"
				src="<?= $image->thumb('extra-images')->url() ?>" 
				width="780" 
				height="520"
			>
		</picture>
	<?php endif ?>

And this is my config.php file

<?php
return [
	'debug' => false,


	'thumbs' => [
		'presets' => [
			'extra-images' => ['width' => 780, 'height' => 520, 'crop' => 'center'],
			'news-images' => ['width' => 780]
		],

		'srcsets' => [
			'extra-images-webp' => ['width' => 780, 'height' => 520, 'crop' => 'center', 'format' => 'webp'],
			'news-images-webp' => ['width' => 780, 'format' => 'webp'],
		]
	]
];

I’m just getting a PHP error
Block error: “Call to a member function url() on string” in block type: “event”

Okay turns out the srcset doesn’t require ->url()

I now have

<?php if ($image = $block->event_photo()->toFile()): ?>
	<picture>
		<source srcset="<?= $image->srcset('extra-images-webp') ?>" type="image/webp">
		<img 
			alt="<?= $image->alt() ?>"
			src="<?= $image->thumb('extra-images')->url() ?>" 
			width="780" 
			height="520"
		>
	</picture>
<?php endif ?>

But now the original jpg format is being displayed rather than the webp version and the cropping is not happening.

This works. The <source> srcset needs to have sizes defined. And the config file secrets also needs to have sizes defined.

This seems strange because I am not using responsive images – I’m not generating different size images for different viewports.

I presume '780w' refers to 780px width? And then in my block sizes="780" references this? Or do I need to reference the same name sizes="780w'?

<?php if ($image = $block->event_photo()->toFile()): ?>
	<picture>
		<source
			srcset="<?= $image->srcset('extra-images-webp') ?>"
			sizes="780"
			type="image/webp"
		>
		<img 
			alt="<?= $image->alt() ?>"
			src="<?= $image->thumb('extra-images')->url() ?>" 
			width="780" 
			height="520"
		>
	</picture>
<?php endif ?>

<?php
return [
	'debug' => true,


	'thumbs' => [
		'presets' => [
			'extra-images' => ['width' => 780, 'height' => 520, 'crop' => 'center'],
			'news-images' => ['width' => 780]
		],

		'srcsets' => [
			'extra-images-webp' => [
				'780w'  => ['width' => 780, 'height' => 520, 'crop' => 'center', 'format' => 'webp']
			],

			'news-images-webp' => [
				'780w'  => ['width' => 780, 'format' => 'webp']
			],
		]
	]
];

Hmm now this is a HTML question more than a Kirby question.

I think you can use <picture> and <source srcset> with a single URL in the srcset attribute. The sizes attribute is only required if you use width descriptors in srcset. If you only want to use <picture> to offer a different source type, you should be able to do something like this:

<picture>
	<source
		srcset="path/to/image.webp"
		type="image/webp"
		width="780"
		height="520" />
	<img
		alt="(…)"
		src="path/to/image.jpg"
		width="780"
		height="520" />
</picture>

In that case, you probably want to use the $image->thumb() method instead of $image->srcset() to generate the URL for the srcset attribute. (Because Kirby’s srcset method seems geared towards a “responsive images” use case, and your use case is different.) So it would look like:

<picture>
	<source
		srcset="<?= $image->thumb('extra-images-webp')->url() ?>"
		type="image/webp"
		width="780"
		height="520" />
	<img
		alt="(…)"
		src="<?= $image->thumb('extra-images')->url() ?>"
		width="780"
		height="520" />
</picture>

(You would need to move the 'extra-images-webp' config to the 'presets' section instead of 'srcsets'.)

Hey, thank you, I’ll give that a go. Small thing, the width and height only need to be specified on the <img> element