DEV Community

Cover image for Quick Start: Eleventy Shortcodes for Responsive Images & Inline SVG
Quesby
Quesby

Posted on • Originally published at quesby.dev

Quick Start: Eleventy Shortcodes for Responsive Images & Inline SVG

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>
Enter fullscreen mode Exit fullscreen mode

Quick Start

Follow these steps to get ready-to-use shortcodes in minutes:

  1. Install dependency
   npm i @11ty/eleventy-img
Enter fullscreen mode Exit fullscreen mode
  1. Create the shortcodes module at eleventy/shortcodes/media.js (see below)

  2. Register it in eleventy.config.js and rebuild

  3. Use {% 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/
Enter fullscreen mode Exit fullscreen mode

Install

npm i @11ty/eleventy-img
Enter fullscreen mode Exit fullscreen mode

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);
};
Enter fullscreen mode Exit fullscreen mode

Register in Eleventy config

In eleventy.config.js (ESM):

import mediaShortcodes from "./eleventy/shortcodes/media.js";

export default function(eleventyConfig) {
  eleventyConfig.addPlugin(mediaShortcodes);
}
Enter fullscreen mode Exit fullscreen mode

Usage in Nunjucks

Responsive image (async shortcode):

{% image "hero/cover.jpg", "Site cover image", "(min-width: 768px) 75vw, 100vw" %}
Enter fullscreen mode Exit fullscreen mode

Inline SVG with a custom class:

{% svg "assets/icons/github.svg", "icon-lg text-neutral-700" %}
Enter fullscreen mode Exit fullscreen mode

Alternative: expression style

If you prefer {{ ... }} output for SVG, also register:

// in the plugin function
cfg.addShortcode("svg", svgShortcode);
Enter fullscreen mode Exit fullscreen mode

Then:

{{ svg("assets/icons/github.svg", "icon-lg") | safe }}
Enter fullscreen mode Exit fullscreen mode

Troubleshooting and notes

  • “Unable to call ‘svg’…” means you registered with addShortcode but used {% svg %}. Use addNunjucksShortcode for tag syntax.
  • Paths: keep inputs relative to src/…. The module normalizes src/assets/images/... automatically for images and src/... for SVGs.
  • Accessibility: alt is required. Use empty alt only for decorative images.
  • Fallbacks: if you need a bitmap fallback, add "jpeg" to formats.
  • Caching: @11ty/eleventy-img caches 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.resolve and 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; }
Enter fullscreen mode Exit fullscreen mode

Commit message template

feat(shortcodes): add responsive image and inline SVG helpers (Nunjucks, ESM)
Enter fullscreen mode Exit fullscreen mode

Top comments (0)