Writing images for the modern web

If you're not using the WebP format and you are writing images as a simple img tag with only a jpg src attribute, you're missing out on speed improvements, accessibility and SEO.
What, why and how to use a custom Laravel blade component with a fallback.

Alt, lazy and dimensions

You already know that you have to add an alt attribute to your image tag for the accessibility and SEO of your website. But did you know that you should also set the image width and height attributes? This is to avoid layout shift while your image hasn't been loaded yet. This can be disturbing for the people reading your webpage, especially those on a slow connection. You can overwrite the dimensions with css to support responsive layouts. For more information, check this article on web.dev

The support for the loading="lazy" attribute is currently around 74%. It is easy to implement and has an immense benefit in the speed/bandwidth consumed as the images are only downloaded when they are in the user's viewport.

Use the WebP format with a fallback

WebP is the new format that provides superior lossless and lossy compression for images on the web.
They are much smaller (25 – 35%) with the same quality. The support for WebP is great among modern browsers but it is probably best to provide a jpg fallback. That can be achieved with the following code:

<figure>
    <picture>
        <source srcset="https://mywebsite.com/dog.webp" type="image/webp">
        <img src="https://mywebsite.com/dog.jpg" alt="picture of a dog" width="640" height="360" loading="lazy">
    </picture>
</figure>

Creating a blade component

I thought this could be a nice blade component. Imagine the following blade code to display an image.

<img src="{{ asset('files/src/'.$img->file) }}" class="box-shadow img-fluid"  height="450" alt="{{ $img->title }}"/>

What if we could use the new WebP format but with the fallback for the older formats without repeating ourselves and with the least amount of work (developers are a lazy bunch after all ;). Let's create a blade component called "Image":

php artisan make:component Image 

Paste the following code in App\View\Components\Image.php:

namespace App\View\Components;

use Exception;
use Illuminate\Contracts\View\View;
use Illuminate\View\Component;

class Image extends Component
{
    public ?string $webpFileUrl = null;
    public ?string $fallbackFileUrl = null;
    public ?string $errorMessage = null;

    public function __construct(string $src)
    {
        try {
            $mediaInfo = pathinfo($src);
            $filename = $mediaInfo['filename'];
            $dirname = $mediaInfo['dirname'];
        } catch (Exception $exception) {
            return $this->errorMessage = 'Could not load blade component with src "' . $src . '" ('. $exception->getMessage() . ')';
        }
        $this->webpFileUrl = $dirname . '/' .  $filename . '.webp';
        $this->fallbackFileUrl = $src;
    }

    public function render(): View
    {
        return view('components.image');
    }
}

This is the new way of working with components since Laravel 7. Paste the following code in App\View\Components\Image.php:

@if(isset($errorMessage))
    {{ $errorMessage }}
@else
    <figure>
        <picture>
            <source srcset="{{ $webpFileUrl }}" type="image/webp">
            <img src="{{ $fallbackFileUrl }}" {{ $attributes }} loading="lazy">
        </picture>
        {{ $slot }}
    </figure>
@endif

Now, all you need to do, is to change the "img" with "x-image". This assumes that you have your WebP images in the same directory as your regular images but you could easily create another implementation. $slot will write any content that you place inside your x-image tags, for instance a figcaption tag. Nifty!