Enhance Image is a Single File Component (SFC) for Enhance that makes authoring responsive images easy, by providing a simple component API backed by an on-demand image transformation service.
Responsive images are an important aspect of responsive web design and development. They offer major performance improvements by delivering appropriately sized images to a range of device resolutions and viewport sizes so that, for example, a mobile phone doesn't waste time and bandwidth downloading an image sized for a widescreen monitor.
Implementing responsive images, however, can be challenging. Preparing multiple variants of images can be tedious and time consuming, and the web platform APIs for using responsive images in the browser can be tough to wrap your head around. This is where Enhance Image comes in. By combining a simple custom element interface with an on-demand image transformer, it saves time on image preparation and reduces the overhead of implementing responsive images correctly.
Install Enhance Image by running the following command in your Enhance project:
npm install @enhance/image
Then, add enhance/arc-image-plugin
to your project’s .arc
file, under the @plugins
pragma:
@plugins
enhance/arc-image-plugin
This enables Enhance Image’s image transformation service (which is used by the component).
The image transformation service works by taking a source image from your project and generating new variants of it by applying transformations based on size, image format, and image quality.
These configuration options can be specified in your project's Preflight file, under the plugins
key. For example:
// app/preflight.mjs
export default async function Preflight ({ req }) {
return {
plugins: {
'@enhance/image': {
transform: {
widths: [2400, 1200, 800],
format: 'webp',
quality: 80,
},
}
}
}
}
The above configuration will tell the image transformation service that, for every source image passed to the Enhance Image component, it should generate three variants: one at 2400px wide, one at 1200px wide, and one at 800px wide (while preserving your images' intrinsic aspect ratios). Each of those variants will be generated in the webp format, at a quality setting of 80%.
(Coincidentally, the above configuration is the default for the image transformation service, and may be omitted from your Preflight file if this works well enough for you.)
In more detail:
widths
(optional)format
(optional)quality
(optional)To use the SFC, first import it from the package, and then re-export it to make it available to your app:
// app/elements/enhance-image.mjs
import EnhanceImage from '@enhance/image'
export default EnhanceImage
You can then use the SFC in your pages and other elements, for example:
<h1>My Favourite Dog</h1>
<enhance-image src="https://github.com/enhance-dev/enhance-image/raw/main/_public/images/dog.jpg" alt="My favourite dog"></enhance-image>
The SFC accepts the following attributes, which are proxies for the same named attributes of the Image element:
src
alt
sizes
(optional)width
and height
(optional)loading
(optional)fetchpriority
(optional)The transformation service that powers Enhance Image creates variants of your images on demand — that is, when they are requested by the browser of an end user visiting your site. These images are cached for performance once created. However, the initial network request for each image variant (before it’s cached) can take longer to return than may be ideal, especially for larger or complex images.
For this reason, Enhance Image includes a script for ‘warming’ your image caches, which ensures that each image is cached (and will thus be loaded as quickly as possible) before an end user ever requests it.
This script is available to run via npx
, as follows:
npx @enhance/image warm --directory /public/images --domain https://example.org
The script takes two arguments:
--directory
--domain
Note: The warming script may take some time to complete its initial run. On subsequent runs, images that have already had their caches warmed in previous runs will be skipped, making subsequent runs much faster.
We recommend integrating the cache warming script with GitHub Actions in order to automate this functionality. (The script can be run from your terminal, however this can be easily forgotten on subsequent deployments.)
A GitHub Action workflow for running this script could look (in part) like this:
name: CI
on: [push]
jobs:
deploy:
name: Deploy (production)
# …
steps:
# …
- name: Warm image caches
run: npx @enhance/image warm --directory /public/images --domain https://example.org
This will ensure that your image caches are warmed each time a production deploy is made.
<enhance-image
src="https://github.com/enhance-dev/enhance-image/raw/main/_public/images/dog.jpg"
alt="My favourite dog"
></enhance-image>
Using only the required attributes, and presuming no custom configuration has been specified in your Preflight file, this will result in the following:
sizes
<enhance-image
src="https://github.com/enhance-dev/enhance-image/raw/main/_public/images/dog.jpg"
alt="My favourite dog"
sizes="(min-width: 48em) 50vw, 100vw"
></enhance-image>
Again presuming the default configuration is being used in this example, the browser will select the generated image closest to 50% of the current viewport width when the viewport is 48em or wider. At viewports narrower than 48em, the image closest to the full width of the current viewport will be used.
sizes
<enhance-image
src="https://github.com/enhance-dev/enhance-image/raw/main/_public/images/dog.jpg"
alt="My favourite dog"
loading="lazy"
sizes="auto"
></enhance-image>
This is a good strategy to use for images that are rendered outside of the initial viewport area of a page. Since loading of the image will be deferred until the browser anticipates the image will be needed, the browser will already understand what size of image will be needed for the given content area, and can thus automatically determine which of your generated images to use.
// app/preflight.mjs
export default async function Preflight ({ req }) {
return {
plugins: {
'@enhance/image': {
transform: {
widths: [1280, 1024, 720, 480, 375],
quality: 60,
}
}
}
}
}
format
option has been specified, the default format (webp) will be used<enhance-image
src="https://github.com/enhance-dev/enhance-image/raw/main/_public/images/post-assets/dog.jpg"
alt="My favourite dog"
sizes="(min-width: 96em) 25vw, (min-width: 48em) 50vw, 100vw"
></enhance-image>
25vw
in width will be used; at viewports between 48–96em wide, the generated image closest to 50vw
in width will be used; at viewports narrower than 48em, the image closest to the full width of the current viewport will be usedsizes
attributeThe sizes
attribute used by Enhance Image is simply a proxy for HTMLImageElement.sizes
. This sizes
attribute, as specified by the HTML living standard, can be tough to wrap your head around in certain cases. However, we believe it’s beneficial to remain close to the web platform, as it avoids the need to learn brittle abstractions and thus keeps knowledge reusable. As such, when you learn how to work with Enhance Image’s sizes
attribute, you’re actually learning how to work with the Image element’s sizes
attribute — warts and all.
Below are several considerations to keep in mind when using sizes
.
When multiple media conditions are listed, the first media condition that matches the viewport will be used, and all others will be ignored. Therefore, if using min-width
media queries, these should be written in descending order, e.g. (min-width: 72em) 25vw, (min-width: 48em) 50vw
. Conversely, max-width
media queries should be written in ascending order.
When deciding which of your generated images to use for a given media condition in sizes
, the browser evaluates multiple factors — not just the viewport width, but also the current display’s pixel density, and potentially other factors as determined by the particular browser.
Additionally, it’s important to keep in mind that the lengths specified in the sizes
attribute do not represent a desired image width, but rather the width of the content area the image is meant to occupy.
Therefore, if you specify an absolute length in your sizes
attribute (for example: (min-width: 48em) 400px
), the browser may not select an image that is closest to 400px in width. If, for example, the browser is currently running in an environment with a 2x pixel density, it will likely select an image closer to 800px in width, in order to fill a 400px wide content area with an image of a suitable size given a 2x pixel density display.
For this reason, it’s often advantageous (and makes for a clearer implementation) to specify the lengths in your sizes
attribute using vw
units.
For further details, see MDN’s documentation on the sizes
attribute, or their documentation of the img
element.
We’ve published a full walkthrough for Enhance Image. Watch the video here!
The following updates are either planned or currently in active development: