Brett Gullan

Digital product developer, designer and technologist.

Simplifying Responsive Images with A Responsive Configuration Builder - Eliminate Boilerplate

Simplify the process of specifying responsive image urls in your front-end javascript projects.

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!
Specifying all these derivatives quickly becomes a challenge, not to say a right pain in the a**. No matter what environment you’re working in, whether it’s more traditional server-rendering using a templating environment such as Twig or Nunjucks, or modern javascript frameworks such as React or Vue, the problem remains, and is essentially the same:
How to specify the enormous number of image variants required for a responsive website, in a succinct, repeatable — and ideally reusable — fashion?
Responsive Configuration Builder (RCB) is thus the result of my experience, and frustration, with this situation. And I hope, at least the beginnings of an answer.

Who is it for?

RCB is designed as a utility library for front-end developers using javascript rendering. So it should work in almost any environment — Nunjucks, Handlebars, Angular, Vue, React, etc.
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
props
to a component.
Here’s a quick example:
What’s going on here?
In this very simple example, we import and initialize a
TokenBuilder
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:
  1. It expands spec object shorthand formatting; and,
  2. It resolves the spec object into a final url string, or set of url/descriptor pairs.
In the example above, the
srcset
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.
The entire result is returned as a plain old javascript object (with all resolved urls as string values) ready to be emitted using whatever templating or view rendering you like.
On its own, this may not look like much. The real benefit — IMO — comes when this approach is applied to more complex structures.
Take this sandbox, for example. (Go ahead, I'll wait!)
Here we’re using the built-in
CloudinaryBuilder
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
src
,
srcset
,
data-srcset
,
placeholder
,
lqip
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.

Motivation

The primary motivation behind Responsive Configuration Builder was to provide a succinct, composeable and reusable method for generating responsive images. After all, RCB consumes and produces plain old javascript objects, and can thus be easily incorporated into almost any javascript environment, pipeline or framework.
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 principles, right?
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.

Inspiration

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 and Imager 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.

Conclusion

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.

Next Steps

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.

References

Tags

Comments

Topics of interest