I have been replacing my existing site images with .webp. And in HTML it's relatively easy to add fallback support for instance in which .webp isn't supported. However, I'm struggling to find an equivalent for markdown?
HTML
<img src="img.webp" />
HTML with fallback support
<picture>
<source srcset="img.webp" type="image/webp">
<source srcset="img.jpg" type="image/jpeg">
<source srcset="img.png" type="image/png">
</picture>
For Example
Markdown

Markdown with fallback support

I don't think there's a spec for fallback images like that in markdown, but if you're using markdown-it
(which is the default markdown parser in Eleventy) you can add custom plugins to extend the syntax. You can also just put HTML in your markdown, or you can even use shortcodes in your markdown if you're using Eleventy.
You can define custom shortcodes in Eleventy and then use them in your markdown. This could be easier and more flexible than setting up markdown-it
plugins, but has the drawback of putting non-markdown things in your markdown.
markdown-it
pluginIf you already have pre-generated images that you want to use, you may want to use something like markdown-it-picture
and modify it to set the type
attribute, either by parsing the file extension or changing the media query/title spot to accept the type
instead. You'll want to copy the code into a new file in your project, make your modifications, and register it like so:
// .eleventy.js
module.exports = function(eleventyConfig) {
const mdPicture = require('/path/to/plugin.js')
const md = require('markdown-it')()
md.use(mdPicture)
eleventyConfig.setLibrary('md', md)
// ...
};
eleventy-image
Alternatively, if you don't have images already generated and would like to generate them as part of the 11ty build step, you can use eleventy-image
(Docs). This Twitter thread has a discussion on creating a custom markdown-it renderer, which I've modified to use with eleventy-image
below.
// you might want to put this in another file
// such as ./utils/markdown.js
// responsive images with 11ty image
// this overrides the default image renderer
// titles are also used for size setting (optional)
//
//  100vw, 768px')
const md = require('markdown-it')();
const Image = require('@11ty/eleventy-img')
md.renderer.rules.image = function (tokens, idx, options, env, self) {
const token = tokens[idx]
let imgSrc = token.attrGet('src')
const imgAlt = token.content
// you can modify the default sizes, or omit
const imgSize = token.attrGet('title') || '(max-width: 768px) 100vw, 768px'
const widths = [250, 426, 580, 768] // choose your own widths, or [null] to disable resize
const imgOpts = {
widths: widths
.concat(widths.map((w) => w * 2)) // generate 2x sizes for retina displays
.filter((v, i, s) => s.indexOf(v) === i), // dedupe widths
formats: ['webp', 'jpeg'], // choose your own formats (see docs)
urlPath: '/assets/img/', // src path in HTML output
outputDir: './_site/assets/img/' // where the generated images will go
}
// generate the images
// see https://www.11ty.dev/docs/plugins/image/#synchronous-usage
Image(imgSrc, imgOpts)
const metadata = Image.statsSync(imgSrc, imgOpts)
return Image.generateHTML(metadata, {
alt: imgAlt,
sizes: imgSize,
loading: 'lazy',
decoding: 'async'
})
}
// in your .eleventy.js
module.exports = function(eleventyConfig) {
// you may need to `require` the file with
// your markdown config with the custom renderer
// const md = require('./utils/markdown.js')
eleventyConfig.setLibrary('md', md)
// ...
};
This renderer will automatically generate different sizes of your image, as well as different formats, and then output the <picture>
tags in your HTML. Since markdown-it doesn't play well with async
functions, we use the syncronous pattern of eleventy-image
. More information about configuration can be found in the docs.
One thing you might have to be careful about is that the image path in your markdown should be where the image is located in your website source, NOT the eventual published URL. For example, my image may be located at src/images/img.png
, but published at https://example.com/images/img.png
. The markdown should be 
and NOT 
. You can also do additional parsing in the renderer function.