This guide shows how to add two production-ready shortcodes to Eleventy (ESM): a responsive image helper using @11ty/eleventy-img and an inline SVG helper that lets you inject CSS classes.
Here’s the final output generated in the DOM (fully handled by the shortcode):
<picture>
<source type="image/avif" srcset="/img/hero_320.avif 320w, ..." />
<source type="image/webp" srcset="/img/hero_320.webp 320w, ..." />
<img src="/img/hero_original.webp" alt="Site cover image" loading="lazy" />
</picture>
Quick Start
Follow these steps to get ready-to-use shortcodes in minutes:
- Install dependency
npm i @11ty/eleventy-img
Create the shortcodes module at
eleventy/shortcodes/media.js(see below)Register it in
eleventy.config.jsand rebuildUse
{% image %}for responsive images and{% svg %}for inline SVG in your Nunjucks templates
What you get
The {% image %} shortcode outputs a <picture> element with AVIF/WebP sources, responsive widths, lazy loading, and decoding hints—all preconfigured for production use.
Prerequisites
- Eleventy v2+ or v3 with ESM config.
- Nunjucks templates (examples use
{% %}tags). - Project layout with assets under
src/.
Example folders:
src/
assets/
images/
icons/
Install
npm i @11ty/eleventy-img
Shortcodes module
Create eleventy/shortcodes/media.js:
import path from "node:path";
import fs from "node:fs";
import Image from "@11ty/eleventy-img";
/** Responsive image (async) */
async function imageShortcode(src, alt, sizes = "100vw") {
if (!alt) throw new Error(`Missing alt for ${src}`);
// Normalize to src/assets/images/
const normalized = src.replace(/^\/?src\/assets\/images\/?/, "");
const resolved = path.resolve("src/assets/images", normalized);
const metadata = await Image(resolved, {
widths: [320, 640, 960, 1280, null], // null => original width
formats: ["avif", "webp"], // add "jpeg" if you want a fallback
outputDir: "./_site/img/",
urlPath: "/img/",
});
return `<figure>${Image.generateHTML(metadata, {
alt,
sizes,
loading: "lazy",
decoding: "async",
})}</figure>`;
}
/** Inline SVG with optional class */
function svgShortcode(svgPath, className = "") {
const rel = svgPath.replace(/^\/?src\/?/, ""); // keep paths under src/
const fullPath = path.join(process.cwd(), "src", rel);
const svg = fs.readFileSync(fullPath, "utf8");
if (!className) return svg;
// Append or set class on the opening <svg ...>
const hasClass = /<svg([^>]*?)\sclass="([^"]*)"/i.test(svg);
if (hasClass) {
return svg.replace(
/<svg([^>]*?)\sclass="([^"]*)"([^>]*)>/i,
(_m, pre, cls, post) => `<svg${pre} class="${cls} ${className}"${post}>`
);
}
return svg.replace(/<svg([^>]*)>/i, `<svg$1 class="${className}">`);
}
export default (cfg) => {
cfg.addNunjucksAsyncShortcode("image", imageShortcode);
cfg.addNunjucksShortcode("svg", svgShortcode);
};
Register in Eleventy config
In eleventy.config.js (ESM):
import mediaShortcodes from "./eleventy/shortcodes/media.js";
export default function(eleventyConfig) {
eleventyConfig.addPlugin(mediaShortcodes);
}
Usage in Nunjucks
Responsive image (async shortcode):
{% image "hero/cover.jpg", "Site cover image", "(min-width: 768px) 75vw, 100vw" %}
Inline SVG with a custom class:
{% svg "assets/icons/github.svg", "icon-lg text-neutral-700" %}
Alternative: expression style
If you prefer {{ ... }} output for SVG, also register:
// in the plugin function
cfg.addShortcode("svg", svgShortcode);
Then:
{{ svg("assets/icons/github.svg", "icon-lg") | safe }}
Troubleshooting and notes
-
“Unable to call ‘svg’…” means you registered with
addShortcodebut used{% svg %}. UseaddNunjucksShortcodefor tag syntax. -
Paths: keep inputs relative to
src/…. The module normalizessrc/assets/images/...automatically for images andsrc/...for SVGs. -
Accessibility:
altis required. Use emptyaltonly for decorative images. -
Fallbacks: if you need a bitmap fallback, add
"jpeg"toformats. -
Caching:
@11ty/eleventy-imgcaches processed assets. Commit the cache if you want stable builds in CI.
Additional tips:
- ESM vs CJS configs: This example assumes ESM. If you are on CommonJS, adapt imports/exports accordingly.
-
Windows paths: Prefer
path.resolveand avoid hardcoded backslashes. The provided code handles normalization. - External URLs: The shortcode expects local files. For remote images, download them during build or provide a local copy.
Minimal CSS example
.icon-lg { width: 1.5rem; height: 1.5rem; display: inline-block; }
Commit message template
feat(shortcodes): add responsive image and inline SVG helpers (Nunjucks, ESM)
Top comments (0)