Optimizing Page Load Perf with @11ty/eleventy-img
Preference is saved in browser LocalStorage
Have an 11ty site? Have images? @11ty/eleventy-img makes it easy to speed up your site.
What's the problem?
As an image format, PNGs are great. The clean, crisp images are easy on the eyes. Transparency is great. Modern screenshot tools create them by default.
Unfortunately, PNG doesn't have a great compression ratio, so the images might be larger than necessary.
New formats like WebP and AVIF have been created to solve this problem. According to Can I Use, browser support for WebP is excellent now in 2022 (even in Safari on Mac), so I want to give it a try.
I'd like to still create PNG, but have my website build process automatically create optimized images. Better yet, I'd like to load normal JPG or PNG when the client does not support WebP.
Enter @11ty/eleventy-img.
Measure before optimizing
Loading SkaterDad.dev with the Firefox Network tab open, we see the game preview images are pretty hefty. Apple Spider's PNG is about 550 KB. Santa Skate's is 148 KB. Even the background texture of the pages is 155 KB!
We can do better.
Implementing Eleventy Image
First, follow the official documentation to install the package.
Now, in your .eleventy.js
config file, add the code to create the image shortcodes. This will let you easily use the plugin from your templates or markdown files.
The code below is adapted from the official example.
I had to add the outputDir
and urlPath
keys to the metadata
object to make this work for my site, which outputs everything to a dist
directory.
NOTE: Apparently the urlPath
must end in a slash. When I left off the final slash, the URL was still defaulting to /img
. YMMV.
const Image = require("@11ty/eleventy-img");
async function imageShortcode(src, alt, sizes) {
let metadata = await Image(src, {
widths: [null],
formats: ["webp", "jpeg"],
outputDir: "./dist/images/optimized",
urlPath: "/images/optimized/",
});
let imageAttributes = {
alt,
sizes,
loading: "lazy",
decoding: "async",
};
return Image.generateHTML(metadata, imageAttributes);
}
module.exports = function (eleventyConfig) {
eleventyConfig.addNunjucksAsyncShortcode("image", imageShortcode);
eleventyConfig.addLiquidShortcode("image", imageShortcode);
eleventyConfig.addJavaScriptFunction("image", imageShortcode);
// ......
// the rest of your config
};
Use the image shortcode in Nunjucks & Markdown
My blog uses Nunjucks and Markdown. To get super-meta, this is how I added the first image of this article to the .md
file. You could do the same in a .njk
file.
<figure>
{% image "./src/site/images/11ty-img/network_before.png", "Network tab image
downloads. Much Kilobytes" %}
<figcaption>Network tab image downloads. Much Kilobytes.</figcaption>
</figure>
Use the image shortcode in a Nunjucks Include
Things get complicated if you are using the shortcode inside of a Nunjucks Include.
On my home page, I iterate through a list of games in the Nunjucks template. Within the loop, I bring in an include
which uses the new shortcode. Lucky me, nothing rendered, and there were no error messages in the console. Admittedly, I have not taken the time to properly learn Nunjucks...
Many Googles later, I found the solution in the plugin Github issues.
Instead of iterating with a for
block, you need to use the Nunjucks asyncEach
block. Why did Nunjucks create more syntax for this instead of just allowing for
to do async stuff? Ugh.
-- game-teaser.njk
<li>
{% image game.image, game.name %}
</li>
-- other-file.njk
<ul class="grid xl:grid-cols-2 gap-4">
{% asyncEach game in games %}
{% include 'components/game-teaser.njk' %}
{% endeach %}
</ul>
HTML Output
The shortcode generates a <picture>
element with a preferred source of the WebP image, and fallback to a JPG.
For further reading on the <picture>
element, check out the docs on MDN.
<picture>
<source
type="image/webp"
srcset="/images/optimized/yyrJj3ScWb-512.webp 512w"
/>
<img
alt="Network tab image downloads. Much Kilobytes"
loading="lazy"
decoding="async"
src="/images/optimized/yyrJj3ScWb-512.jpeg"
width="512"
height="174"
/>
</picture>
Front page results
After applying the image optimizations to the home page background and game feature images, we can check the Network tab again.
We achieved a 75% reduction in image sizes. 🌟 Great success! 🌟
Scenario | Kilobytes |
---|---|
Before | 933.21 KB |
After | 233.4 KB |
Savings | 699.81 KB |
Check out my other blog posts and games!
Want the latest updates? Follow me on Twitter!