Simplifying Responsive Images with A Responsive Configuration Builder - Eliminate Boilerplate
Digital product developer, designer and technologist.
Responsive images are a critical element of any modern, fast website. Tools and services abound for creating responsive images — npm scripts, webpack and babel plugins, grunt and gulp tasks, and in particular, platforms such as Cloudinary, Imgix and Kraken (to name just a few). Similarly, there are plenty of tools and tutorials for optimising images, generating low-resolution inline placeholders, and such.
And of course, every CMS or static site generation tool worthy of the name wouldn’t be complete without its own particular take on image management. Amidst this plethora of (often so called) solutions I continually found myself bumping up against a seemingly trivial, but ultimately important problem … specifying all the image variants required for all the responsive images used throughout a website.
Take for example, a standard article image on TheGuardian.com
. This is rendered as a <picture> element that uses 11 different image variants; and as an article thumbnail using another seven. NYTimes.com
uses a responsive <img> element for its article images, with four variants across srcand srcset attributes.
Taking top place though is Vox.com
. The featured article image on its homepage uses a <picture> element with a total of 33 different image variants. The same image, displayed on the article page uses another 17 different variants (again, a <picture> element). With a few more variations for thumbnails of different sizes, that’s more than 50 different versions of the same image!
How to specify the enormous number of image variants required for a responsive website, in a succinct, repeatable — and ideally reusable — fashion?
Who is it for?
Personally, I’ve applied this to projects using Nunjucks for server-side rendering, and for React-based projects using Next.js and Gatsby.
In the Gatsby case, React Configuration Builder helped to combine data pulled via GraphQL from a headless CMS, with images hosted on Cloudinary, for rendering using React.
How does it work?
As the name suggests, RCB is a configuration builder. In the simplest terms, it takes a specification and some image data, and combines the two to build a configuration object that can be consumed by almost any templating or component environment.
For the React-minded reader, the resulting configuration object can be passed directly as
to a component.
Here’s a quick example:
What’s going on here?
In this very simple example, we import and initialize a
instance, using a template string. Then set up a spec object and some image data, passing them to the builder for processing.
The builder then performs two key tasks:
- It expands spec object shorthand formatting; and,
- It resolves the spec object into a final url string, or set of url/descriptor pairs.
In the example above, the
field uses a spec object shorthand — the desired widths are specified using an array. So during the expand
phase the builder converts this into three separate width/height specifications.
Finally, the builder resolves the expanded specification, combining it with the provided image data and previously configured template string to generate the three url/descriptor pairs required.
On its own, this may not look like much. The real benefit — IMO — comes when this approach is applied to more complex structures.
Here we’re using the built-in
to process a more complex spec object, for a complete <picture> element.
Notice that RCB will pass through any spec values it doesn’t recognize. So any media, sizes, alt or title attributes can be included in the spec.
The spec object can also include a top-level options object Any key/value pairs specified here are merged with the individual specifications.
This just helps to reduce boilerplate and redundant data — i.e. keeping specs DRY. The image object also functions a bit like an options object; any key/value pairs supplied here, will also be merged with the individual specifications, before resolving the final output.
Finally, it’s worth noting that RCB will process — i.e. expand and resolve — any attribute value it identifies as a specification. It doesn’t matter whether the specification key is
or anything else. If the builder recognizes the specification, it will process it. In this fashion, RCB spec objects can be used to generate configuration (or props) for almost any image type, including most lazy-loading techniques.
More importantly however, is the architectural separation that RCB promotes.
The actual specification of sizes and variants required for a particular element (picture or image) is rightly a front-end concern. The specification should be co-located with the view layer or templates responsible for rendering the image markup.
The source of any images, whether a local filesystem or external image service, is environment-specific and will typically vary from site to site, and across local development, staging and production. So domain, hostname, port, API key and secret, etc., should be externally configurable (from environment variables). Because of course, we’re all following 12-factor
Finally, the specific image to be rendered is the concern of the content management or data layer. This layer should be largely ignorant of the particular image service in use, and of presentation concerns. There are exceptions of course, but for the most part only minimal image data should be persisted within the content layer.
Enough to uniquely identify the image, provide any additional metadata — such as alt text, title, captioning and copyright information. In some cases it may be appropriate to include some additional rendering hints, such as focal point — to aid automatic cropping.
In extreme cases, it may be necessary to include quite explicit rendering data — crop sizing, scaling and bounding box coordinates. Highly art-directed hero images, for instance. This is still entirely consistent with the design and intent of Responsive Configuration Builder.
So far, in my use of RCB, I’ve found that it helps to maintain the separation of concerns described above. Moreover, it helps prevent problems, I’ve all too frequently encountered — where domain knowledge trickles down, deep into component hierarchies, polluting components and making reuse and maintenance near impossible.
Having used Craft CMS
as my platform of choice for a number of different websites — big and small — I have appreciated, and variously depended on, three excellent third-party plugins. ImageOptimize
both simplify the creation of image variants. And importantly, create them from configuration objects.
But above all, the excellent Picture
plugin from Marion Newlevant captured the essence of a specification format for responsive images. Marion’s plugin crystallised an approach I had previously been fumbling towards, and has subsequently provided the foundation for Responsive Configuration Builder.
If you’re building sites that use responsive images — especially <picture> elements—I’d encourage you to give Responsive Configuration Builder a try. At the very least, it should help to make specifying all those srcset resolutions easier. It might even help to simplify your component composition trees, providing a cleaner separation of your image handling code.
In a future article, I’ll detail the different specification formats, and associated expander functions. As well as demonstrating how to use RCB to implement a custom builder.
Subscribe to get your daily round-up of top tech stories!