Skip to content

How I used Cloudinary to solve responsive image needs in my Jekyll website, and shared the magic in a plugin

This is a guest post by Nicolas Hoizey, co-founder of Clever Age and creator of the Jekyll Cloudinary plugin. Nicolas’ plugin leverages Cloudinary’s image storage, optimization, resizing, and delivery infrastructures to automate responsive images in Jekyll-generated static sites. We think it’s the bee’s knees, and invited Nicolas to write a bit about the process and motivation behind it. Without further ado, here’s Nicolas.

I recently migrated my personal website to the latest major version of Jekyll, and took the opportunity to re-think my toolchain.

Some of the plugins that I’d been using were not satisfying my needs for responsive images, so I decided to find other ways to fulfill these needs.

To generate the responsive images HTML (do I really need to say that using native responsive images should be a no-brainer nowadays?), I tried the Jekyll Responsive Image plugin. It is really nice; it lets you define your own image markup templates, which means you can use srcset or <picture> as you like. But it didn’t address all of my concerns:

  • Generating a Jekyll site from scratch with this plugin requires generating all images’ variants from the master images. I currently have around 750 images on my blog, leading to some very long build times.
  • Sending all these variants to the server also takes quite some time, because I don’t have fast network access at home.
  • And of course, all these images are served from the same server as the pages, which in my case is cheap (but nice) shared hosting.

I wanted a simplified and faster workflow, and less load on the server side.

Most of the responsive web sites that my company builds for clients use ad-hoc solutions for responsive images, but I was aware of a few SaaS responsive image solutions, so I decided to check if any of them would fit my needs.

Cloudinary is one of the most feature-rich solutions available, and it can even be used for free if your needs are reasonable. It’s not easy for other solutions to compete with that…

With a free account, I could test whatever I wanted, try different features, and decide to continue or look elsewhere.

The main features I was looking for, which Cloudinary provides, are:

  • The ability to use the service as a proxy: the master images are stored on my hosting, but all images delivered to my visitors come from Cloudinary, generated on the fly from the masters. Even better, I don’t need to manually upload the master images – Cloudinary fetches them automatically from my local, published versions. In other words, the only “client” for my originals is Cloudinary. As a result, the bandwidth for images on my hosting is really low.
  • Image cropping and resizing options: right now, I am only scaling my images down from large masters to fit responsive layouts. But I’m definitely looking at the possibility of doing advanced art direction using Cloudinary’s magical automatic cropping features.
  • Image format optimization: if I publish JPEG images in my posts, Cloudinary can send WebPs to visitors if their browser supports them. Last month, two thirds of the images delivered by Cloudinary to my visitors were WebPs, which Cloudinary generated and served for me, automatically. That’s a huge win for both my visitors’ performance and data plans, and for my Cloudinary bandwidth quota.
  • Image compression optimization: Cloudinary is able to compute the best compression level to lower the weight of each image while keeping the visual quality high.

Convinced that Cloudinary offered everything I needed, I still had to develop a Jekyll plugin that used these features.

After some consideration, I decided to start with a {% cloudinary %} Liquid tag that would ease image publication with Cloudinary, while still being relatively easy to develop. I took inspiration from other plugins, found help on StackOverflow when needed, and finally released the first version of the Jekyll Cloudinary plugin in July 2016.

The syntax is pretty straightforward:

{% cloudinary [preset] path/to/img.jpg alt="alt text" caption="image caption" %}
Code language: JavaScript (javascript)

From this input, the plugin outputs responsive image HTML, using the srcset and sizes attributes for the <img> tag (see the “varying size and density” section of this post to understand how these attributes work, and this post which explains why you should use them instead of <picture>, most of the time). The srcset and fallback src contain Cloudinary URLs that fetch the post’s master images on-the-fly and resize them to several, alternate sizes.

For example, as shown in the documentation, this code in a Markdown file:

{% cloudinary logo /assets/logos/cloudinary.png alt="Cloudinary logo" %}
Code language: JavaScript (javascript)

will generate this HTML code:

<img
  src="https://res.cloudinary.com/<cloud_name>/image/fetch/c_limit,w_480,q_auto,f_auto/https://<domain>/assets/logos/cloudinary.png"
  srcset="
    https://res.cloudinary.com/<cloud_name>/image/fetch/c_limit,w_80,q_auto,f_auto/https://<domain>/assets/logos/cloudinary.png 80w,
    https://res.cloudinary.com/<cloud_name>/image/fetch/c_limit,w_240,q_auto,f_auto/https://<domain>/assets/logos/cloudinary.png 240w,
    https://res.cloudinary.com/<cloud_name>/image/fetch/c_limit,w_400,q_auto,f_auto/https://<domain>/assets/logos/cloudinary.png 400w"
  sizes="
    (min-width: 50rem) 13rem,
    (min-width: 40rem) 25vw,
    45vw"
  class="logo"
  alt="Cloudinary logo"
  width="480"
  height="350"
/>
Code language: HTML, XML (xml)

You are in full control of the number of generated images, their resolutions, and the sizes attribute (which helps the browser decide which image to download). This control comes from the configuration options you can use in your _config.yaml. Here is the part of my configuration file where I defined rules for logos:

cloudinary:
  cloud_name: …
  presets:
    logo:
      min_width: 80
      max_width: 400
      fallback_max_width: 200
      steps: 3
      sizes: '(min-width: 50rem) 13rem, (min-width: 40rem) 25vw, 45vw'
      figure: never
      attributes:
        class: logo
  • cloud_name: … is your personal ID from Cloudinary
  • presets: starts the list of presets you define for your site
  • logo: is the name of one of my presets, which I use in the Liquid tag before the name of the image file
  • min_width: 80 defines the narrowest generated image
  • max_width: 400 defines the widest generated image
  • fallback_max_width: 200 defines the fallback (src) image width
  • steps: 3 defines the number of images to generate
  • sizes: '(min-width: 50rem) 13rem, (min-width: 40rem) 25vw, 45vw' defines the sizes attribute of the responsive image, which depends on the design and breakpoints
  • figure: never prevents generation of a full <figure>/<img>/<figcaption> element (I often don’t want them on logos)
  • attributes: starts a list of attributes to always add to generated <figure> and/or <img> elements
  • class: logo adds a class attribute with a logo value, which I use in my CSS to make sure the logo never takes more than one fourth of its container width, and is floated right

You can define all of these rules, for any number of presets that you need.

With this plugin and my free Cloudinary account, the build time of my site has been reduced by 90%, the storage on my server has been reduced by 60%, and I don’t have to worry about image optimization anymore. Huge wins.

Where to go from here? Originally, I wanted to allow writers to use the simple standard Markdown syntax for images, but I have not managed to build this yet, despite some valuable answers to my questions from Jekyll lead maintainer Parker Moore himself. I’ll have to dig deeper into Jekyll hooks in the future.

At the end of the day, this was a great way to learn a bit of Ruby, a bit about the Jekyll internals, how plugins work, and how to build and release a gem… I’ve learned so much in a short period of time thanks to this useful little pet project.

Of course, help is welcome to make the plugin even better. There are already a few issues open for bugs or things that should be added to the plugin. Feel free to add your bug reports and ideas, or even contribute through pull requests!

Back to top

Featured Post