11ty Excerpts and You

17 June '20

So you've got some Really Radical Content(tm) and you want to show a bit of ankle in 11ty. That's all fine and dandy but it turns out that this can be a little trickier than you may think at first blush.

Option 1: TemplateContent

If you've done any poking around online you may have run into solutions using a custom shortcode to read templateContent. While this might work for some (and will probably be your best bet in the future) I was running into sorting issues when using this method. Reversing / sorting the collection I was grabbing excerpts from failed the second any of my code called post.templateContent. I'm still not sure why, but I'm going to chalk it up to the growing pains of a young tool (v0.11 as I'm typing this.)

Either way, in case you're living in the future or you're only getting a single excerpt, here's the code I was using for this.

// eleventy.js
const getExcerpt = (post) => {

if (!post.hasOwnProperty('templateContent')) {
console.warn('Post has no property templateContent - unable to extract excerpt.')
return null
}

let excerpt = null;
const content = post.templateContent;
const separators = [
{ start: '<!-- start excerpt -->', end: '<!-- end excerpt -->'},
{ start: '<p>', end: '</p>'}
];

separators.some((separator) => {
const startPos = content.indexOf(separator.start);
const endPos = content.indexOf(separator.end);
if (startPos !== -1 && endPos !== -1) {
excerpt = content.substring(startPos + separator.start.length, endPos).trim();
return true;
}
})
return excerpt;
}

module.exports = (function(eleventyConfig) {
eleventyConfig.addShortcode('excerpt', (post) => getExcerpt(post))
}

If you want to dig a little deeper on this method, check out this post.

Option 2: Gray-Matter

According to the docs, we've been able to parse excerpts from content since v0.9. This is super handy. It lets us add a customizable tag (such as <!-- more -->) to our content in order to auto-generate an excerpt without needing to write any shortcodes. The issue I ran into with this, however, is that I often start my posts with a header. This was getting pulled into the excerpt because we can only specify the end of the excerpt. If this is a non-issue for you, great! This is the 'official' way to set up excerpts on your site.

// eleventy.js
module.exports = (function(eleventyConfig) {
eleventyConfig.setFrontMatterParsingOptions({
excerpt: true,
excerpt_separator: "<!-- more -->"
})
}

As a fun aside, I wasn't able to use this solution at first because the excerpt data wasn't yet available in post.data.page. That became available shortly after I picked this back up.

Option 3: Front-Matter + Gray-Matter

In order to handle the case where I start a post with a title, I added the option to explicitly set the excerpt in front-matter. From there I wrote a quick shortcode to grab the excerpt from post.data.excerpt (front-matter) or post.data.page.excerpt depending on availability. Add in some markdown rendering so links and the like still work and we're off to the races.

// eleventy.js
const markdownIt = require('markdown-it');

const getExcerpt = (post) => {
const md = new markdownIt({ html: true })
if (post.data.excerpt) {
return md.render(post.data.excerpt);
}
else if (post.data.page.excerpt) {
return md.render(post.data.page.excerpt);
}
return null
}

module.exports = (function(eleventyConfig) {
eleventyConfig.addShortcode('excerpt', (post) => getExcerpt(post))
eleventyConfig.setFrontMatterParsingOptions({
excerpt: true,
excerpt_separator: "<!-- more -->"
})
}

Option 4: Paired Shortcodes in Markdown?

This isn't something I've fully explored, but I'd love to be able to use paired shortcodes in markdown for something like this. If templateContent didn't wreck collection sorting that's essentially what we'd be doing with option 1, but I'd love if something like this were supported out of the box. In the meatime, if templateContent decides to work or someone comes up with a way to bypass it in option 1 I'll be switching over to that solution.