Using 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.