Eleventy: Paired Shortcodes and Markdown Rendering

Update 2020-06-29: If you’re implementing responsive images, I suggest reading my follow-up on using Nicolas Hoizey’s Eleventy Images Responsiver plugin. Everything below still applies if you’re just looking for general notes on Eleventy’s Paired Shortcodes.

The first time I read the documentation on Eleventy’s Paired Shortcodes I couldn’t quite figure out how I would use one, so I stuck to a regular shortcode for generating figure/image output. Here’s what my original shortcode looked like:

{% figure "DSCF1433.jpg" "This is a caption" "cinemascope" %}

This worked quite well! Except that it was starting to get a little bit ungainly. I needed to support the following params:

  • Image name
  • Alt text
  • Caption
  • Optional CSS class

Jamming all of that into a standard shortcode was starting to get ungainly.

Paired Shortcodes

Paired shortcodes let you nest template content inside, so it felt like a good attempt to separate the caption as text content from the other params. Here’s what I tried:

eleventyConfig.addPairedShortcode("figure", (data, image, altText, styleName) => {
    const styleObj = {
      cinemascope: [
        { width: 1200, breakwidth: 1000 },
        { width: 850, breakwidth: 650 },
      ],
      book_thumb: [{ width: 300, breakwidth: 400 }],
      default: [{ width: 850, breakwidth: 650 }],
    };

    const styleItem = styleObj[styleName]
      ? styleObj[styleName]
      : styleObj["default"];
    const classMarkup = styleName ? ` class="${styleName}"` : "";
    data = data.trim();
    if (data !== undefined && data !== "") {
      captionMarkup = `<figcaption>${data}</figcaption>`;
    } else {
      captionMarkup = "";
    }
    let srcsetMarkup = "";
    styleItem.forEach((element) =&gt; {
      srcsetMarkup += ``;
    });
    return `<figure>${srcsetMarkup}<img src="/img/${image}?nf_resize=fit&amp;w=400" alt="${altText}" />${captionMarkup}</figure>`;
  }
);

In my Markdown file I could then do this:

{% figure "DSCF1433.jpg", "Barn with distant trees barely visible in the fog.", "cinemascope" %}
Testing if **Paired Shortcodes** can take *Markdown* transformations
{% endfigure %}

The comma-delimited params in the opening {% figure %} tag map to the image name, alt text, and optional CSS class. The text between the {% figure %} and {% endfigure %} is the caption, and also the first parameter passed into the callback function (as data). This worked well enough, except that any Markdown in the caption was ignored and rendered as plain text. So the above would result in this for the caption:

<figcaption>Testing if **Paired Shortcodes** can take *Markdown* transformations</figcaption>

Turns out this is a common pitfall. Any text that gets wrapped in HTML before being returned doesn’t get run through Markdown parsing, if I understand that documentation note correctly. But there’s a workaround! I followed a clue in this Github issue, and looking at the example it appears that using markdown-it’s renderInline() method will do the transformation, and then you can wrap the output in HTML. So, adding this line:

data = markdownLibrary.renderInline(data);

did the trick. Here’s the full code:

eleventyConfig.addPairedShortcode("figure", (data, image, altText, styleName) =&gt; {
    const styleObj = {
      cinemascope: [
        { width: 1200, breakwidth: 1000 },
        { width: 850, breakwidth: 650 },
      ],
      book_thumb: [{ width: 300, breakwidth: 400 }],
      default: [{ width: 850, breakwidth: 650 }],
    };

    const styleItem = styleObj[styleName]
      ? styleObj[styleName]
      : styleObj["default"];
    const classMarkup = styleName ? ` class="${styleName}"` : "";
    data = data.trim();
    if (data !== undefined &amp;&amp; data !== "") {
      data = markdownLibrary.renderInline(data);
      captionMarkup = `<figcaption>${data}</figcaption>`;
    } else {
      captionMarkup = "";
    }
    let srcsetMarkup = "";
    styleItem.forEach((element) =&gt; {
      srcsetMarkup += ``;
    });
    return `<figure>${srcsetMarkup}<img src="/img/${image}?nf_resize=fit&amp;w=400" alt="${altText}" />${captionMarkup}</figure>`;
  }
);