Back to main page Traveling Coderman

Injecting an HTML snippet into every 11ty post


If you render your post with {{ content | safe }} in your layout, then it shows up as a continuous post without interruption. You can render additional markup before or after the post with Before post {{ content | safe }} After post. However, in some cases, you might want to show additional markup within the post itself. This way, it catches the attention after the visitor decided to read the post but before they fully read the post.

In this post, I show how you can inject such additional markup at a specified position into each post.

Injecting a snippet 🔗

Instead of rendering the content directly with {{ content | safe }}, we assign the additional markup to a variable and pass it and the content to a filter inject.

{% set snippet %}
{% include "_includes/my-snippet.njk" %}
{% endset %}

{{ content | inject(snippet) | safe }}

This filter inject takes the content and the snippet and injects the snippet at a specific position into the content.

Since the content is a string with HTML, we need to parse it. We use the library cheerio for that. It allows us to parse, manipulate and serialize HTML.

npm install --save-dev cheerio

The inject filter 🔗

In the eleventy config .eleventy.js, we import cheerio and define the filter inject. It consists of three steps: Parse, manipulate and serialize.

const cheerio = require("cheerio");

module.exports = function (eleventyConfig) {
// ...
eleventyConfig.addFilter("inject", (content, injection) => {
const $ = cheerio.load(content);
$("body > h2:nth-of-type(3)").before(injection);
return $.html($("body"));
// ...

By default, cheerio adds a body around the HTML. To ensure the same output structure as input structure, we instruct cheerio to serialize the content of the body tag with $.html($("body")). In the manipulation, we use the existence of the body tag to only select the direct descendents of it. For my use case, I selected the third headline in each post and instructed cheerio to add the additional markup in front of it.

With these steps, you will get an injected HTML snippet into every post.

Pitfall: Asynchronous short code 🔗

With the usage of include within a variable assignment block set, you need to use synchronous Nunjucks filters within the snippet my-snippet.njk.

{% set snippet %}
{% include "_includes/my-snippet.njk" %}
{% endset %}

In my use case, this was relevant to render an optimized image.

{% illustration "./assets/my-illustration.svg" %}

The Eleventy documentation includes an example of how to define a synchronous short code for images.