DEV Community

webbureaucrat
webbureaucrat

Posted on • Originally published at webbureaucrat.gitlab.io on

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 as easy as I expected. 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 within 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 a .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 post lists

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

(Fair warning: I formatted this for readability. If you copy/paste this, you may see some whitespace issues to fix.)

{% 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.

Click here for a live demo.

Top comments (0)