Welcome! We notice you're using an outdated browser, which will result in a degraded experience on this site. Please consider a modern, fully supported browser.

webbureaucrat

The articles are just window-dressing for code snippets I want to keep.

How to Use Excerpts in Eleventy

Recently, I added first-paragraph post excerpts to this Eleventy blog's homepage post list. I found it wasn't easy. It wasn't all documented all in one place. Further, in order to use Markdown excerpts in HTML, I had to write a simple custom filter. I'd like to document the process here from end to end.

Enable grey-matter excerpts in .eleventy.js.

The first thing we need is to configure Eleventy to be able to see our excerpts. This is easily done by adding this line to the .eleventy.js configuration file into the main module.exports = function(eleventyConfig) { ... } function.

eleventyConfig.setFrontMatterParsingOptions({ excerpt: true
                                            });

Optional: Set the excerpt separator

The excerpt separator is some string which marks the end of the excerpt and the beginning of the rest of the article. By default, it is "---" but this default is easily overridden using the optional excerpt_separator property of the Front Matter parsing options object, like so:

eleventyConfig.setFrontMatterParsingOptions({ excerpt: true,
                                              excerpt_separator: "--excerpt--"
                                            });

Add excerpt separators into each post

Now you can mark each post with excerpt separators. I use the default "---", but you can use whatever excerpt_separator you may have overridden it with in the previous step.

Write a simple custom filter to read Markdown excerpts

At this point, we need to reconcile a potential conflict. In my case at least, I am writing blog posts in markdown, but my post list on the homepage is an .njk that compiles to HTML. I don't want to change either of those things, but if I reference my markdown excerpts in HTML, they'll show up as raw markdown text. I need to write a bit of middleware to reconcile the two.

Start by locating your markdownIt options object. You'll find it in a block of code that looks something like this:

let markdownLibrary = markdownIt({
            html: true,
            breaks: false,
            linkify: true
        });
eleventyConfig.setLibrary("md", markdownLibrary);

You'll need to reuse this object, so it's a good practice to separate it into its own constant at the top of the file...

const MARKDOWN_OPTIONS =
      {
          html: true,
          breaks: false,
          linkify: true
      };

...that can be referenced in multiple places

let markdownLibrary = markdownIt(MARKDOWN_OPTIONS);
eleventyConfig.setLibrary("md", markdownLibrary);

This is all in the service of writing a simple custom filters that can take markdown strings and turn them into HTML fragments. This will do it for us.

eleventyConfig.addFilter("toHTML", str => {
    return new markdownIt(MARKDOWN_OPTIONS).renderInline(str);
});

Use the custom filter to display the HTML excerpts in your postlists

Now, finally, I can include this excerpt in my /_includes/postlist.njk, which iterates over the posts collection like so:


    {% for post in postslist | reverse %}
    <li class="postlist-item{% if post.url == url %} postlist-item-active{% endif %}">
        <h2 class="h2-postlist">
            <a href="{{ post.url | url }}"
               class="postlist-link">
                {% if post.data.title %}{{ post.data.title }}
                {% else %}<code>{{ post.url }}</code>
                {% endif %}
            </a>
        </h2>
        <time class="postlist-date"
              datetime="{{ post.date | htmlDateString }}">
            {{ post.date | htmlDateString }}
        </time>
        {% for tag in post.data.tags %}
            {%- if collections.tagList.indexOf(tag) != -1 -%}
                {% set tagUrl %}/tags/{{ tag }}/{% endset %}
                <a href="{{ tagUrl | url }}" class="tag">{{ tag }}</a>
            {%- endif -%}
        {% endfor %}
    </li>
    {% endfor %}

Let us pipe the post excerpt into our toHTML custom filter and then pipe the output of our filter to the built-in safe filter so that Eleventy treats the output as HTML instead of plain text.


    {% for post in postslist | reverse %}
    <li class="postlist-item{% if post.url == url %} postlist-item-active{% endif %}">
        <h2 class="h2-postlist">
            <a href="{{ post.url | url }}"
               class="postlist-link">
                {% if post.data.title %}{{ post.data.title }}
                {% else %}<code>{{ post.url }}</code>
                {% endif %}
            </a>
        </h2>
        <time class="postlist-date"
              datetime="{{ post.date | htmlDateString }}">
            {{ post.date | htmlDateString }}
        </time>
        {% for tag in post.data.tags %}
            {%- if collections.tagList.indexOf(tag) != -1 -%}
                {% set tagUrl %}/tags/{{ tag }}/{% endset %}
                <a href="{{ tagUrl | url }}" class="tag">{{ tag }}</a>
            {%- endif -%}
        {% endfor %}
        {%- if post.data.page.excerpt -%}
            <p>{{ post.data.page.excerpt | toHTML | safe}}</p>
        {%- endif -%}
    </li>
    {% endfor %}

And now when we run npx eleventy --serve we should see our excerpts everyplace we reference _includes/postlist.njk.

I write to learn, so I welcome your constructive criticism. Report issues on GitLab.

← Home