Modify Markdown Image Output
Markdown itself supports only a few attributes for embedding images. Instead of plugins or shortcodes, let's extend the renderer!
The first option to set more attributes for an image than src
and alt
in Markdown is using a plugin (something like markdown-it-attrs). While this is doable, it should be possible to extend the markup without repeating every attribute for each image manually, right?
The second option is to bypass the regular Markdown syntax, e.g. by using a shortcode. This option offers the advantage of full flexibility, but if you use an editor or CMS to create the Markdown files, you usually have to extend the CMS itself and bypass feel-good features like the image preview.
However, there is a third way to dynamically change the output of Markdown images. When parsing Markdown via markdown-it
you can hook into the renderer and determine the output manually.
Basic Example
A basic example of this would look like the following.
Note: The markdown-it
renderer does not support asynchronous functions, so we have to limit ourselves to synchronous processing.
let md = require("markdown-it")();
md.renderer.rules["image"] = function (tokens, idx, options, env, self) {
const token = tokens[idx];
const src = token.attrGet("src");
const alt = token.content;
return `<img src="${src}" alt="${alt}" loading="lazy" />`;
}
Responsive Markdown Image using 11ty Image
With a library like 11ty Image you can now perform much more complex optimizations based on the data. In this example we create responsive images from Markdown data.
let md = require("markdown-it")();
md.renderer.rules["image"] = function (tokens, idx, options, env, self) {
try {
const token = tokens[idx];
const alt = token.content;
let src = token.attrGet("src");
if (src.startsWith("/assets")) {
src = "input/directory/" + src;
}
const imageOptions = {
widths: [320, 480, 600, 720, 1200, "auto"],
formats: ["webp", "avif"],
urlPath: "pass/this/path/to/output/url",
outputDir: "output/image/here",
filenameFormat: function(id, src, width, format, options) {
const extension = path.extname(src);
const name = path.basename(src, extension);
return `${name}-${width}w.${format}`;
}
};
Image(src, imageOptions);
let metadata = Image.statsSync(src, imageOptions);
let sizes = "(min-width: 800px) 45rem, 90vw";
return Image.generateHTML(metadata, {
sizes,
alt,
loading: "lazy",
decoding: "async",
});
} catch (e) {
console.error(e);
}
};