-
Notifications
You must be signed in to change notification settings - Fork 9
Responsive image support
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).
- 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)
- 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 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.
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.
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.
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.
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.
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%.
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.
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.
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:
- 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
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:
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">
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.
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>
Got a question or need help? We have a forum on Github Discussions where you can get in touch with us.