Jekyll responsive images with srcset

Ivo Valchev

22 May 2018

First things first, let me define the term “responsive image”

“Responsive image” can be quite easily confused within the context of responsive design, not least because Google’s first search result points out to a w3schools tutorial on, well, responsive images that scale up and down on different screen sizes.

Now what I am talking about in terms of a responsive image is serving different images in HTML for different situations depending on device breakpoints. (is there a different term for this?!)

So, what’s the big deal you may ask? Well, optimisations. Have you not been in the situation where you’ve completed a simple (or not) website and wanted to benchmark your outstanding coding abilities against Page Insights? You’ve got great HTML structure, perfectly optimised and minimised CSS and JS, enabled browser caching and… then, the thing spits out 63/100 with “Optimise your images” as the first optimisation suggestion?

“That’s it, I’m out, won’t ever write a single line of code again!!1!” (Oh, perhaps that’s just me…)

Anyway, this can be changed and responsive images is one (of several) image optimisation techniques for this purpose.

Also, we’re not using a CDN

A CDN solution like Cloudinary can do all of today’s work for us. But assuming all you are using Jekyll for is your blog, portfolio or tool documentation, it just feels a bit excessive to deploy a CDN service for such a limited purpose. Plus, for me a lot of the beauty that comes with using static site generators for small projects is, quite literally, your repository being the world to you - it’s just all in there, nice and neat!

This is not to say CDNs shouldn’t be used, but let’s not over-engineer small projects.

TL;DR We’ll use the srcset attribute of <img> to optimise image delivery. Not using a CDN.

By the end of the article:

  • your site will automatically generate responsive images based on a source image
  • Jekyll will scan your markdown for images and generate the <img> HTML tag so it uses the srcset attribute with the responsive images (this means you only set this up once and not need to worry about it later - yay!)

Jekyll responsive images plugin

First of all, lets install the jekyll-responsive-images plugin. All it does is generate a set of different-sized images from a source image, which we will then use in the srcset attribute.

Adding the plugin to your site

Locate your Gemfile and insert the following line (next to your other plugins):

gem "jekyll-responsive-image"

Then add another entry to your plugins array in _config.yml:

- jekyll-responsive-image

Customising the plugin configs

Now we need to customise the plugin’s configuration. A sample configuration is available below, which you can copy and paste inside _config.yml. Twist the configs as you like (here is a useful article for src sizes in particular), just make sure you’re using the responsive-image.html template, which we will create next.

responsive_image:
  # [Required]
  # Path to the image template.
  template: _includes/responsive-image.html #THIS ONE

  # [Optional, Default: 85]
  # Quality to use when resizing images.
  default_quality: 90

  # [Optional, Default: []]
  # An array of resize configuration objects. Each object must contain at least
  # a `width` value.
  sizes:
    - width: 480  # [Required] How wide the resized image will be.
      quality: 80 # [Optional] Overrides default_quality for this size.
    - width: 800
    - width: 1400
      quality: 90

  # [Optional, Default: false]
  # Rotate resized images depending on their EXIF rotation attribute. Useful for
  # working with JPGs directly from digital cameras and smartphones
  auto_rotate: false

  # [Optional, Default: false]
  # Strip EXIF and other JPEG profiles. Helps to minimize JPEG size and win friends
  # at Google PageSpeed.
  strip: true

  # [Optional, Default: assets]
  # The base directory where assets are stored. This is used to determine the
  # `dirname` value in `output_path_format` below.
  base_path: uploads

  # [Optional, Default: assets/resized/%{filename}-%{width}x%{height}.%{extension}]
  # The template used when generating filenames for resized images. Must be a
  # relative path.
  #
  # Parameters available are:
  #   %{dirname}     Directory of the file relative to `base_path` (assets/sub/dir/some-file.jpg => sub/dir)
  #   %{basename}    Basename of the file (assets/some-file.jpg => some-file.jpg)
  #   %{filename}    Basename without the extension (assets/some-file.jpg => some-file)
  #   %{extension}   Extension of the file (assets/some-file.jpg => jpg)
  #   %{width}       Width of the resized image
  #   %{height}      Height of the resized image
  #
  output_path_format: assets/resized/%{width}/%{basename}

  # [Optional, Default: true]
  # Whether or not to save the generated assets into the source folder.
  save_to_source: false

  # [Optional, Default: false]
  # Cache the result of {% responsive_image %} and {% responsive_image_block %}
  # tags. See the "Caching" section of the README for more information.
  cache: false

  # [Optional, Default: []]
  # By default, only images referenced by the responsive_image and responsive_image_block
  # tags are resized. Here you can set a list of paths or path globs to resize other
  # images. This is useful for resizing images which will be referenced from stylesheets.
  extra_images:
    - uploads/*/*/*/*.{jpeg,jpg}

Adding the responsive image template

So we now have the plugin fully configured! But we are not quite done yet. To actually use it to generate our HTML, we need to create the responsive-image.html template we specified earlier. Create the file inside your project’s _includes folder and add the following content:

{% capture srcset %}
    {% for i in resized %}
        /{{ i.path }} {{ i.width }}w,
    {% endfor %}
{% endcapture %}
<img src="/{{ path }}" alt="{{ alt }}" srcset="{{ srcset | strip_newlines }}">

By now you the plugin is fully set up and usable with the responsive_image tag as provided in its documentation. However, having to use the tag every time for every single image is quite annoying and requires you to write code. More importantly, this will not really work with markdown images.

Suppose you are using a frontend-friendly CMS to generate your content, having to manually change the HTML output is simply not feasible and impossible if you want to let content writers do their job. This is why we are…

Automating responsive images in markdown too

To do this, we’ll use Jekyll hooks. Basically they allow you to intervene in the site generation process to tweak it here and there so it lets you achieve a specific purpose.

If you don’t already have one, create a _plugins folder at the root level of your site. Inside, create a img-tag-transform.rb file and copy and paste the following:

Jekyll::Hooks.register :posts, :pre_render do |post, payload|
  docExt = post.extname.tr('.', '')
  post.content.gsub!(/!\[(.*)\]\(([^\)]+)\)(?:{:([^}]+)})*/, '{% responsive_image path: \2 \3 %}' )
  post.content.gsub! 'path: ', 'path: ' #you can probably optimise this a bit
end

This bit of code basically replaces markdown images in your posts with the responsive image tag, which then triggers the plugin we set up earlier.

We’re done!

If you made it all the way to here, well done! And thank you. Hopefully this has been helpful to you and please let me know if you have any questions or suggestions for improvements (I’m a learner still, and will probably be for the rest of my life)!

Till next time. 😉