Skip to content

Responsive image support

Waseem Sadiq edited this page Jul 14, 2022 · 4 revisions

Responsive image support in pages has been implemented through an image template filter which transforms a standard html <img> element to a responsive image element.

It provides automatic lazy-loading, preloading of critical images, correct sizing across devices, and automatically supports modern formats with images from any source (including remote images).

Caveats

  1. This filter doesn't implement actual image optimisation, it only transforms the image element (when the page is generated) and injects the necessary scripts, headers etc. Image optimisation is provided by using Joomlatools Server which works seamlessly with this filter, or can be provided by any other external service. (For more info see below)
  2. The filter has been carefully crafted to offer 100% compatibility with all modern browsers. While the code provides forward compatibility for newer browser features such a Client Hints and Native Browser Lazy loading it doesn't rely on them as defaults.

Images on the Web

Images take up 50% of the total bytes on web pages. Half of all images we use on the web are over one megabyte in size, which means they aren't optimized to be displayed on the web. Images have a big impact on Largest Contentful Paint(LCP) as they're often the largest visible element when a page is loaded. LCP is a Core Web Vital that Google will be using in their search ranking very soon.

Nowadays users browse the web using their phones, tablets, and laptops, yet images are still as a one size fits all. For example: sites load a 2000 by 2000 pixel image, but phones are only displaying it as 100 by 100 pixels. Tim Kadlec has estimated that — for users on low-res mobile devices — 72% of image bytes served up by responsive pages are wasted. Responsive pages are sending small-screened users nearly four times as much data as they need.

Furthermore, 30% of images on web pages are outside of the initial viewport, meaning the browser loads images that a user does not see until they scroll further down the page.

Images often don't have a width and height property, causing them to jump around when the page is loaded and 99.7% of images on websites don't use modern image formats like WebP. All this hurts the Cumulative Layout Shift Core Web Vital badly.

In order to use images on web pages in a performant way a lot of aspects have to be considered: size, weight, lazy loading, device preferences, connection quality, ... and modern image formats.

Developers have to set up complicated build tooling to optimize images, however those tools usually don't cover user-submitted images coming from an external data source, making it impossible to optimize all images.

This impossible development task inevitably leads to a frustrating end-user experience. This PR implements a solid solution to performant and optimised images on the web that takes care of all the above.

Features

Pages help reduce the complexity of maintaining and generating multiple image versions with the dynamic image transformation feature. Pages build the dynamic image URLs automatically for you. This means you don't have to pre-create the images, and your images are dynamically resized on-the-fly as needed.

1. Generation of responsive img markup

At it's most basic it is just a drop-in replacement for the HTML <img> element, evolved for the modern web. Turning this

<img src="/images/profile-picture.jpg" width="400" height="400" alt="Profile Picture">

into this (in its most basic form):

<img srcset="/images/profile-picture.jpg?w=400 1x, /images/profile-picture.jpg?w=400 2x, /images/profile-picture.jpg?w=400 2x" width="400" height="400" alt="Profile Picture">

Image dimensions are enforced, allowing browsers to immediately render the space needed for the image instead of having it jump in when loaded, preventing layout shift.

While width and height on the HTML <img> element can cause issues with responsive layouts, this is not the case when using Pages. The image is automatically made responsive based on the aspect ratio using the actual image size.

2. Smart responsive breakpoint calculation

Having too many image versions reduces the number of CDN cache hits for requested images and increases the average delivery time for images to your users. On the other hand, having too few image versions means delivering images to users that are larger than needed for the available width, and end up being scaled down by the browser.

To solve this Pages calculates the breakpoints for you based on the actual image filesize. It calculates the optimal number of versions needed for every image balancing the number of image versions generated vs. the file size reduction between each version. The set of breakpoints are thus calculated based on a difference in the actual image file size at different widths.

3. Lazy image loading

Images are automatically lazy-loaded using the (Lazysizes)[https://github.com/aFarkas/lazysizes] js library meaning they're only rendered when the user is close to seeing the image. This prevents loading that 30% of images outside of the initial viewport.

4. Preloading images

You can mark images that are in the initial viewport to be automatically preloaded. Preloading images in the initial viewport has shown improvements to the Largest Contentful Paint by up to 50%.

5. Low Quality Image placeholder (LQI)

Pages can generate and insert a LQI placeholder image that will be loaded.

LQIP’s logic is simple. In a sense, this is like loading progressive JPEGs. Initially load the page with low quality images Once the page loaded, replace them with the full quality images.

LQIP gives us the best of both worlds. On a slow connection, the user will load a fully usable page much faster, giving them a significantly better user experience. On a fast connection, the extra download of the low quality images – or the delay of the high quality ones – does not lead to a substantial delay. In fact, even on the fast connection the user will get a usable page faster, and they’ll get the full rich visuals a moment later.

6. Automatic Image Optimisation

Even with these improvements compared to the HTML element, there's still a major problem. The 2000 by 2000 pixel image is sent to phones that render a smaller image.

Pages can automatically generate smaller sizes through built-in Image Optimization by deploying it using Joomlatools Server

Built-in Image Optimization automatically serves the images in modern image formats like WebP, which is about 30% smaller than JPEG, if the browser supports it. It also allows to automatically adopt future image formats and serve them to browsers that support those formats.

Image Optimization works with any image source. Even if images come from an external data source they can be optimized.

Instead of optimizing images at build time, images are optimised on-demand, as users request them. Unlike static site generators and static-only solutions, your build times aren't increased, whether shipping 10 images or 10 million images.

7. Adapts to users device capabilities and preferences

Image Optimisation will consider device capabilities and user preferences to serve the most optimised image possible for the context of the device and or user. It makes use of:

Rules:

  • If the connection is 2g only lazy load nearby images (just outside of the viewport), and the image quality is reduced 2x *If save-data is enabled only load visible images and the image quality is reduced 3x

Getting started

To use the image filter simple add the filter to your page or layout:

---
@layout: template://pages/document.html
@process:
    filters:
        - image
---

This is all you need to do, all images generated for the specific page, or layout will now be transformed to responsive image. Pages makes a difference between two types of images:

Fixed image size

A fixed size image require either the width or the height (or both) attribute(s) to be specified. Pages will then generate a responsive img element using srcset display density descriptors.

For example:

<img src="/images/profile-picture.jpg" width="400" alt="Profile Picture">
<img src="/images/profile-picture.jpg" height="400" alt="Profile Picture">
<img src="/images/profile-picture.jpg" width="400" height="400" alt="Profile Picture">

Flexible image size

For flexible images Pages generates srcset’s width descriptors using automatic generated sizes.

To optimise bandwidth as much as possible, the width descriptors are calculated by downscaling the image based on its actual filesize in steps of 30%, taking into account a max and min image size which can be configured.

Configuration

To provide additional configuration you either add additional data-[option] attributes to the <img> element or you can wrap a html fragment into a <ktml:images> block, and specific additional config option through the element attributes.

lazyload: true|false|progressive|inline (*) preload: true|false width: [int] max-width: [int] height: [int] max-height: [int] max-dpr: [int] () If lazyload="progressive" a Low Quality Image (LQI) will be generated and inserted. () If lazyload="progressive,inline" a Low Quality Image (LQI) will be generated and transformed into a data uri

For example:

<img src="/images/foo.jpg"  width="400" data-lazyload="progressive,inline" />
 <ktml:images lazyload="progressive,inline" width="400">
     <img src="/images/foo.jpg" / >
     <img src="/images/bar.jpg"  />
 </ktml:images>
Clone this wiki locally