Getting the official 11ty image plugin
to work took me something like ten days and an embarrassing amount of outrageously bad language. I don't know why I had to change the parts that I had to change, and neither do I understand how other tutorials seem to make the thing work when I could not, short of the sawing and hacking detailed anon. All I'm saying is that this is my own 11ty image plugin
truth.
Firstly, I'll detail my own adventures. Then, a brief step-by-step on how I got the thing to work in the end.
This is based on the official documentation: https://www.11ty.dev/docs/plugins/image/. I started by setting up an 11ty site as usual, with this standard, recognisable initial configuration.
.
├── node_modules
├── package-lock.json
├── package.json
└── src
├── _includes
│ └── base.njk
├── css
│ └── style.css
└── index.md
And that's all great. Next I installed the plugin:
npm i -D @11ty/eleventy-img
And added some code to .eleventy.js
to make the thing work:
const Image = require("@11ty/eleventy-img");
async function imageShortcode(src, alt, sizes) {
let metadata = await Image(src, {
widths: [300, 600],
formats: ["avif", "jpeg"]
});
let imageAttributes = {
alt,
sizes,
loading: "lazy",
decoding: "async",
}
return Image.generateHTML(metadata, imageAttributes);
}
module.exports = function(eleventyConfig) {
// earlier code here
eleventyConfig.addNunjucksAsyncShortcode("image", imageShortcode);
eleventyConfig.addLiquidShortcode("image", imageShortcode);
eleventyConfig.addJavaScriptFunction("image", imageShortcode);
};
What the code does is detailed in the documentation. There's nothing I can add to that.
Next, I created an index page, index.md
, and called an image like so:
{% image "./src/images/bond.png", "Bond, James Bond", "(min-width: 30em) 50vw, 100vw" %}
This followed the documentation recipe exactly, with one exception: I used a picture of Seán Connery in Goldfinger, wearing the most beautiful suit I've ever seen, rather than some mangy cat. Someday, I hope to own a suit like that. Besides; it's my tutorial, and I can do what I like.
So, here are the files as they were before I ran the program to assemble the site for the first time:
├── node_modules
├── package-lock.json
├── package.json
└── src
├── _includes
│ └── base.njk
├── css
│ └── style.css
├── images
│ └── bond.png
└── index.md
There was only one page in my website, index.md
, and its only purpose is to show a picture of Sean Connery in Goldfinger, enjoying his mint julep. I typed npm start
, I hit Enter, and I navigated to http://localhost:8000 to see how the thing has turned out.
There was no sign of Bond. He was missing in action.
But this wasn't my first rodeo. I right-clicked and chose Inspect
, and found this code where 007 ought to be.
<picture>
<source type="image/avif" srcset="/img/jAjwboTfhA-300.avif 300w, /img/jAjwboTfhA-600.avif 600w"
sizes="(min-width: 30em) 50vw, 100vw">
<source type="image/jpeg" srcset="/img/jAjwboTfhA-300.jpeg 300w, /img/jAjwboTfhA-600.jpeg 600w"
sizes="(min-width: 30em) 50vw, 100vw"><img alt="Bond, James Bond" loading="lazy" decoding="async"
src="/img/jAjwboTfhA-300.jpeg" width="600" height="338">
</picture>
The code is looking for a directory called img
, which doesn't exist. There is no reference to an images
directory as per the example in the documentation.
OK. Let's change images
to img
. That's not hard. And we'll change the call in index.md
to match:
{% image "./src/img/bond.png", "Bond, James Bond", "(min-width: 30em) 50vw, 100vw" %}
That didn't work either. 😞
OK. I'm not panicking. I've been doing this a while now. I've picked up a thing or two along the way. It's not mentioned in the documentation, so maybe there's something missing. I looked at the default code, and noticed this mysterious construction:
eleventyConfig.addPassthroughCopy("./src/css");
Two can play at that game. I added
eleventyConfig.addPassthroughCopy("./src/img");
And ran the thing again. Nothing. No Bond, no nothing. Now, I started getting annoyed. I looked at the setup of the folders, to see if my img
directory has been copied successfully:
.
├── img
│ ├── jAjwboTfhA-300.avif
│ ├── jAjwboTfhA-300.jpeg
│ ├── jAjwboTfhA-600.avif
│ └── jAjwboTfhA-600.jpeg
├── node_modules
├── package-lock.json
├── package.json
├── public
│ ├── css
│ │ └── style.css
│ ├── img
│ │ └── bond.png # Bond, James Bond, is right here!
│ └── index.html
└── src
├── _includes
│ └── base.njk
├── css
│ └── style.css
├── img
│ └── bond.png
└── index.md
Yes, it has - look at it in the public
directory, just where it ought to be.
But hold on a second. What in the name of sufferin' succotash is that img
directory doing in the root directory?
The funky file names contained in this new img
directory, newly-discovered in our root directory like a Stargate or some other artefact from beyond the known world, suggests that img
directory is in fact the img
directory that should exist as part of our public
directory but, for some reason, doesn't.
So at least we know what happened. The next question is: what to do about it?
I don't know what anyone else should do, but this is what I did. I revved up Google and tracked an npm
package that would give me bash-scripting ability. This is the package I found: https://www.npmjs.com/package/shelljs. I installed it:
npm i shelljs
And I then added three lines to .eleventy.js
that would
shelljs
object, called shell
;shell
object to create an img
directory in the public
directory if it doesn't exist already, andimg
directory, created by the 11ty image plugin, to this new public/img
directory.So .eleventy.js
now looks like this:
const Image = require("@11ty/eleventy-img");
const shell = require('shelljs') // 1. My shelljs object
async function imageShortcode(src, alt, sizes) {
let metadata = await Image(src, {
widths: [300, 600],
formats: ["avif", "jpeg"]
});
let imageAttributes = {
alt,
sizes,
loading: "lazy",
decoding: "async",
}
shell.exec('mkdir -p public/img') // create the directory if it doesn't exist already
shell.exec('cp img/* public/img') // copy everything from ./img into ./public/img
return Image.generateHTML(metadata, imageAttributes);
}
module.exports = function (eleventyConfig) {
eleventyConfig.addPassthroughCopy("./src/css");
eleventyConfig.addPassthroughCopy(".img");
eleventyConfig.addWatchTarget("./src/css/");
eleventyConfig.addNunjucksAsyncShortcode("image", imageShortcode);
eleventyConfig.addLiquidShortcode("image", imageShortcode);
eleventyConfig.addJavaScriptFunction("image", imageShortcode);
return {
dir: {
input: "src",
output: "public",
}
};
};
And it worked. The images that you see on this site are served quite capably by the 11ty image plug-in, but questions remain.
eleventyConfig.addPassthroughCopy(".img");
really necessary? I don't know, but I'm too scared to move it. It's not like the code is cluttered that much less if I zap it, to be quite frank.11ty image plugin
Install two packages, and
.
npm i -D @11ty/eleventy-img
npm i shelljs
Add this code to .eleventy.js
:
const Image = require("@11ty/eleventy-img");
const shell = require('shelljs') // 1. My shelljs object
async function imageShortcode(src, alt, sizes) {
let metadata = await Image(src, {
widths: [300, 600],
formats: ["avif", "jpeg"]
});
let imageAttributes = {
alt,
sizes,
loading: "lazy",
decoding: "async",
}
shell.exec('mkdir -p public/img') // create the directory if it doesn't exist already
shell.exec('cp img/* public/img') // copy everything from ./img into ./public/img
return Image.generateHTML(metadata, imageAttributes);
}
module.exports = function (eleventyConfig) {
eleventyConfig.addPassthroughCopy("./src/css");
eleventyConfig.addPassthroughCopy(".img");
eleventyConfig.addWatchTarget("./src/css/");
eleventyConfig.addNunjucksAsyncShortcode("image", imageShortcode);
eleventyConfig.addLiquidShortcode("image", imageShortcode);
eleventyConfig.addJavaScriptFunction("image", imageShortcode);
return {
dir: {
input: "src",
output: "public",
}
};
};
Store images in src/img
. Call them in templates as
{% image "./src/img/someImage.png", "Description of the image", "(min-width: 30em) 50vw, 100vw" %}