Using Eleventy’s official image plugin

Save time and trouble with this intro and code

The what, why, and how of Eleventy Image.

April 17, 2021
Last modified May 29, 2021

Close-up photo of camera lens with colorful mood lighting

It’s been about a year since the first release of Eleventy’s official image-processing plugin, Eleventy Image, but only in the last day or so have I given any serious thought to using it.

That’s certainly not because I had any doubt about its quality.

After all, Eleventy Image comes directly from Eleventy’s creator, Zach Leatherman. Who better than he would know how to write a superior plugin for his own static site generator (SSG)?

Rather, it was because I found the plugin’s documentation, and the few blog posts I found about using the plugin, somewhat less than approachable for those who don’t eat, sleep, and breathe things like, say, JavaScript promises.

Then, last night, I finally decided to give it a try and, shortly before midnight, I’d successfully installed it to my satisfaction on my four Eleventy starter sets.1 2

Now that I’ve gone through that, I offer this post in the hope of making it simpler for even new Eleventy users to understand Eleventy Image. After all, if this old fart can, so can you.

Let’s break it down to three simple questions:
“What?” “Why?” “How?”

What is Eleventy Image?

One of Eleventy’s great strengths is how readily you can expand it, through plugins and other adaptations, to do more, work better, and fit your needs. The Eleventy Image plugin is a particularly apt example thereof. Built atop the super-fast and flexible sharp image processor3, Eleventy Image takes image files you “feed” it and turns them into files that are smaller and more efficient. This makes it easier to serve images that work better with your visitors’ respective devices and connections.

So what? Well . . .

Why do you need Eleventy Image?

One answer to that is: you don’t, if you’re willing to do all that image processing on your own. Certainly, that’s quite possible. You can do it in Photoshop, or Affinity Photo, or GIMP, or what-have-you. But that takes a lot of time and work. And, even if you’re not worried about that part, you may not be able to save files in the space-saving WebP format now accepted by most modern browsers.4

Another answer is: you don’t, if you don’t care about how your visitors experience your site. If you’re fine with every visitor getting the exactly same image file regardless of the visitor’s screen size or connectivity quality, then just put up one version of each image and be done with it.

But that’s really not cool, y’know.

On the other hand, Eleventy Image handles all of this for you, automatically and extremely quickly, every time you build your site.

Now that you know what Eleventy Image is and why it’s a great thing to have, let’s get to actually using it.

How do you use Eleventy Image?

Install it

First, let’s install Eleventy Image in your Eleventy project.

Install the plugin package. In your chosen OS’s terminal interface, enter the command npm i -D @11ty/eleventy-img. This will install (i) the plugin package as a development dependency (-D).

Configure Eleventy to use Eleventy Image. Now that the plugin package is installed, you’ll tell Eleventy that it’s there by going into the project’s .eleventy.js configuration file—preferably at the top, before you get into the file’s module.exports statement (about which we’ll talk soon)—and adding:

const Image = require("@11ty/eleventy-img")
const path = require('path')

That tells Eleventy that, when we refer to Image (note the capital “I”) from here on, we’re talking about the Eleventy Image plugin package. (The path part has to do with an option in the upcoming code; if you already have a require('path') statement in your .eleventy.js configuration file, make sure it comes before the code we’ll add below.)

Update, 2021-05-24: I had neglected to mention the path statement before now; I apologize for the omission and any inconvenience it may have caused you. Thanks to Ruvi Lecamwasam for catching this goof and reporting it to me!

Keep that .eleventy.js file open for editing, as we move to the next step.

Create a shortcode for it

Now that Eleventy knows it has Eleventy Image in its quiver, you can create a shortcode to simplify using this tool when you write Markdown for your site content.

Configurations can vary from user to user, but a more-or-less standard .eleventy.js file keeps its main configuration in a module.exports statement:

module.exports = function (eleventyConfig) {
  // Typical configuration fits
  // in this area and can go on
  // for as long as necessary
}

. . . so, still above that area, create an image shortcode (note the lower-case “i”) by adding the code shown below.

async function imageShortcode(src, alt) {
  let sizes = "(min-width: 1024px) 100vw, 50vw"
  let srcPrefix = `./src/assets/images/`
  src = srcPrefix + src
  console.log(`Generating image(s) from:  ${src}`)
  if(alt === undefined) {
    // Throw an error on missing alt (alt="" works okay)
    throw new Error(`Missing \`alt\` on responsiveimage from: ${src}`)
  }  
  let metadata = await Image(src, {
    widths: [600, 900, 1500],
    formats: ['webp', 'jpeg'],
    urlPath: "/images/",
    outputDir: "./_site/images/",
    /* =====
    Now we'll make sure each resulting file's name will 
    make sense to you. **This** is why you need 
    that `path` statement mentioned earlier.
    ===== */
    filenameFormat: function (id, src, width, format, options) {
      const extension = path.extname(src)
      const name = path.basename(src, extension)
      return `${name}-${width}w.${format}`
    }
  })  
  let lowsrc = metadata.jpeg[0]  
  return `<picture>
    ${Object.values(metadata).map(imageFormat => {
      return `  <source type="${imageFormat[0].sourceType}" srcset="${imageFormat.map(entry => entry.srcset).join(", ")}" sizes="${sizes}">`
    }).join("\n")}
    <img
      src="${lowsrc.url}"
      width="${lowsrc.width}"
      height="${lowsrc.height}"
      alt="${alt}"
      loading="lazy"
      decoding="async">
  </picture>`
}

And, finally, within the module.exports section, add:

  eleventyConfig.addNunjucksAsyncShortcode("image", imageShortcode)
  eleventyConfig.addLiquidShortcode("image", imageShortcode)
  // === Liquid needed if `markdownTemplateEngine` **isn't** changed from Eleventy default
  eleventyConfig.addJavaScriptFunction("image", imageShortcode)

Note: You may want to consult “Default Template Engine for Markdown Files” within the Eleventy documentation. (After experiencing certain otherwise inexplicable glitches with the Eleventy Image examples on which the shortcode above was based, I found that specifying markdownTemplateEngine: 'njk' in .eleventy.js fixed them.)

For each image file you “feed” this shortcode in your Markdown (we’ll explain that part next), it will create new files with:

  • The sizes (in pixels) listed in the widths array. I used sizes ranging from 600 to 1,500 pixels because that’s my preference; but you can enter whatever combination works for you, especially if you have any data about your most frequent visitors’ screen sizes.
  • The file formats listed in the formats array. Here, we’re specifying both WebP and JPEG formats because that covers you with virtually every browser out there.5

Additional note, 2021-04-25: If you have a lot of images you’ll be running through this process, you’d best keep the widths array short, since more entries in widths—and, for that matter, formats—will result in even more files that Eleventy Image will have to create at build time. That can result in not only an extremely slow build but also, for local builds, a suddenly very hot CPU on your computer. Play with this over time and see what combination of image quantity, widths options, and formats options will work best for your project.

The setting for urlPath tells your site’s pages to think of these images as being located in the site’s /images/ folder; and the outputDir setting tells Eleventy Image to copy them to the right location to make that work (given the standard Eleventy output directory of ./site).

Note: The Eleventy Image documentation’s “Use this in your templates” section has other examples you may prefer.

Now, let’s get this show on the road.

Use your new shortcode

The code above assumes—and I recommend—that you keep your Eleventy project’s original image files (the ones the image shortcode will process) in ./src/assets/images/. So let’s say that the image file you want to pop into a post you’re writing is called my-pet-cat.jpg, located in that folder. So type this in your post’s Markdown file:

{% image "my-pet-cat.jpg", "Photo of a cat named Shakespeare sitting on a window sill" %}

Here, you’ve entered what the shortcode considers the src part (“my-pet-cat.jpg”; the code automatically adds ./src/assets/images/ to it so you don’t have to type it every time you use the shortcode), then a comma, and then the alt text (“Photo of a cat named Shakespeare sitting on a window sill”) for screen readers. The src location should be based on wherever .eleventy.js is, since that’s where the shortcode resides as well; and, typically, that’s the top level of an Eleventy project. As you know, the alt text describes for screen readers what the image contains. You must enter that alt text if you want the shortcode to work properly—the image shortcode requires both src and alt to work, as you may have noted in the shortcode’s configuration in .eleventy.js. If an image is only decorative (e.g., a logo or other image that fits that description), it’s perfectly acceptable for the alt to be just "", but you must have an alt entry after the src and that separating comma.

Get a look at the result

Now, just build your site, and Eleventy Image will do its magic everywhere within your site that you’re using the image shortcode. For example, here’s what the shortcode above could produce:

Photo of a cat named Shakespeare sitting on a window sill

Of course, you’ll want to get a sneak peek by running Eleventy in development mode and viewing your site locally, in case you need to adjust your site’s CSS so each resulting image appears as you want.6

Go ye forth and process

Sometimes, I wonder what topic I should explore next when writing for this site. More often than not, it comes out of my own search for knowledge and a frustration with how much time and aggravation a particular search required. That certainly was the case with this post. I’d grown weary of trying to get my head around Eleventy Image from what sources I’d located thus far. So, when I did finally sort of “get” it last night, I knew what my next subject would be.

Eleventy Image will only get better in the future—for all I know, Zach Leatherman may even be planning to put it in Eleventy “core” at some point—but there’s no reason to wait. If you have an Eleventy site and want a simple and effective way to handle your site’s images, use this post as a stepping stone on the way to using Eleventy Image and understanding what it can do for you.


  1. I don’t use it on this site, however, because I let Cloudinary process this site’s images. While Eleventy Image definitely can work with images served from other locations besides your site, and although I greatly admire the elegance and features of Eleventy Image, it can’t begin to match all the image-transformation capabilities you can pack into a Cloudinary URL. ↩︎

  2. Adding Eleventy Image to these starter sets allowed me finally to relieve them of my bespoke imgxfm.js file—which, although it worked well enough, delayed each build for a few seconds. For those who’ve used imgxfm.js in the past with any of my starter sets, I highly encourage you to upgrade to the latest and greatest so you can unburden yourself from that sludge. On the other hand, for any masochists out there who still are interested in the imgxfm.js code but don’t have it, let me know and I’ll provide it. ↩︎

  3. Since I’d built imgxfm.js (see the previous footnote) on sharp, I already knew I was going to like Eleventy Image once I did finally understand it well enough to use it. ↩︎

  4. I discussed this in last year’s “So much for heroes.” ↩︎

  5. For more details on the file formats Eleventy Image can accept (input) and create (output), see its documentation. ↩︎

  6. You also can go back into the shortcode’s definition in .eleventy.js to adjust the sizes parameter so that it works better with your site’s unique layout. See MDN’s thorough explanation of how the sizes setting in CSS affects images’ appearances. ↩︎

Other posts

Next:

Previous: