Site Image Optimization using eleventy-img

Jan 16, 2021
Share on Twitter

Why? permalink

  • I originally began with using eleventy-img for all my assets. But, I was having trouble with the markup and the fact that Pug won't support "on the fly" image handling. I switched out to using cloudinary for everything as a quick win.
  • Drop Cloudinary report in here.
  • Issue was that within a day of deployment, I'd used over 3GB of bandwidth. This was mainly due to video and gif as to be expected. However, I was also not getting consistent image formats to come through. Some would be webp, and some would be jpeg.

Solution? permalink

  • I kinda had this half-baked in already.
  • Generate an image markup lookup. Have the sizes generated and the markup at enhance time and make it available to the preprocessor. This could be a Pug workaround 🤔
  • But, it might also make for better DX
  • What's the best experience for writing content?

Nunjucks shortcode


          ![Image Alt](/src/assets/post.png)
  • Short code provides no visual feedback in a preview. But, does provide an "in" for doing things like captions.

Runtime implications. permalink

  • We could transform everything after the fact.
  • But, we are adjusting the markup which has the potential to break the styling.
  • Runtime transformations allow us to deal with this.
  • However. Pug doesn't like the async stuff so it'll make more sense to do this as a transform.
  • Enhancement via transforms works for markdown documents, etc.
  • But. How do we make this play nice with Storybook? We could do transforms in main.js. When we generate the markup for a story, we can wrap the images in a picture tag. We don't care if we get the hottest format.
  • We could also transform all the images in the media folder predev.

Hurdles permalink

  • Netlify Flat CMS media folder is a bit of a downer
  • Could make it so that all the processing is handled at deploy time. However, the markup generates <picture> tags which throw off the styling...
  • Design? Do you prefer writing a shortcode everywhere or would it be easier to drop a standard image tag in? markdown-it perhaps to add titles? Or is shortcode the cleanest way?
  • If you drop a shortcode in. Then that markup gets transformed which would generate another image... Cacheing should handle this. But, I've seen inconsistent results.
  • Decisions about base styling for pictures. This is a quick win because it should for the most part work as a straight swap. This was the case with the featured pens strip.

          picture img {
  max-width: 100%;
  max-height: 100%;
  object-fit: cover;
  • Running the image optimizations on every build doesn't make sense. It's slow. Can't we generate everything in a node script first and then use a lookup? Swapping DOM elements doesn't "feel" expensive at runtime. But, transforming the entire image library on every save is overkill.

Pros permalink

  • Easier to make on the fly content changes without uploading the asset in one spot, and then copying the resource URL to somewhere else. We can use avif.

Todos permalink

  • Switch out to the media chooser for any referenced images in the CMS. Collections: Pens, blog posts, scraps, generic page heros.
  • Do we need lazysizes? I've switched out to loading="lazy" as default unless loading="eager" is explicitly set.
  • Create a predev node script that generates a markup lookup that can be used by the image enhancer when ELEVENTY_ENV === dev
  • Base style the nested images in pictures. Currently we are applying a nested class which does work but will catch us out eventually.
  • Write up as generic image optimization post. Explain what picture tag does visually.

Results permalink

  • Switch out to using a predev script that generates images and assets for us.
  • Wrap your markup in picture where possible.
  • During dev, do nothing, or return a wrapped img where picture isn't the parentNode.
  • Reduce build times by creating a dynamic .eleventyignore.
  • .avif coming out at a 1/3 of the size in some cases
  • Build times from ~30s -> ~1-2s.