Blog Post Cuts in 11ty

At the time of writing, I haven’t yet decided how I want to publish these blog posts; they’re all just sitting on my computer. (Update: At time of publication, I am using WordPress.) One of the options I’ve considered is 11ty. One of the features I’d like out of wherever I post this is the ability to have a “cut” — that is, the ability to designate some amount of the post to appear in the home page, search results, etc.; and make the rest available only after clicking some “read more” type of link.

WordPress supports this under the name of a “more block”; Dreamwidth/LiveJournal supports these with a custom <cut> element, and I’m sure many other blog platforms worth their salt support this too. But, OK, how do you do this in 11ty?

Tricky answer: It’s not actually configured using 11ty itself. 11ty parses frontmatter out of content files using gray-matter. gray-matter also has a trick up its sleeve: It can look for a delimiter and output everything up to that point as an “excerpt”. The excerpt delimiter defaults to ---, but can be overridden with excerpt_delimiter.

To configure that in 11ty, you’ll need to do that with eleventyConfig.setFrontMatterParsingOptions. For example:

// eleventy.config.js
export default (eleventyConfig) => {
    eleventyConfig.setFrontMatterParsingOptions({
        excerpt: true,
        excerpt_separator: "<!-- cut -->"
    })
}

This is documented in 11ty’s page on Custom Front Matter Options. Now you know! OK, but that’s not the whole story, because if we try to put something like this together, we get some unexpected results:

---
title: Example Blog Post
---
This is my _excerpt_, which introduces you to the post.

<!-- cut -->

This is the rest of the post.

This post may render fine on its own, but in a list…

---
title: Blog Index
---
{% for post in collections.posts %}
<article>
<h1><a href="{{ post.url }}">{{ post.data.title }}</a></h1>
{{ post.page.excerpt }}
</article>
{% endfor %}

The Markdown isn’t getting processed. Using the Render plugin in the most obvious way doesn’t work either:

<article>
<h1><a href="{{ post.url }}">{{ post.data.title }}</a></h1>
{% renderTemplate "md" %}{{ post.page.excerpt }}{% endrenderTemplate %}
</article>

It will end up passing the literal text {{ post.page.excerpt }} into the Markdown processor. No good. To be honest, I have no idea why it’s not getting processed inside — I don’t know about Nunjucks, but I assumed it worked like Jinja2, and I didn’t think Jinja2 had any kind of “literal sections” like that. No matter, in any case; we can pass data in and tell it to pass the data through Nunjucks (again) before Markdown:

{% renderTemplate "njk,md", {"post": post} %}{{ post.page.excerpt }}{% endrenderTemplate %}

And at last, it works!

…or does it? Let’s bring an image into the mix:

curl -o posts/image.svg https://placehold.co/600x400.svg

And put it in our post:

---
title: Post 1
---
This is a **bold** summary of the first post.

![600x400 placeholder image](../image.svg)

<!-- cut -->

This is some more detail that you won't see unless you click.

Looks fine on the post itself, but go to the post index, and it’s once again all broken. The URLs are relative to the post’s normal home, not to where it is now accessed from. The 11ty image plugin doesn’t save you either, at least when configured as a transformer: it applies after the templating is done, when the path is still wrong. At least it fails at generation time rather than letting your web site’s viewers see a broken image.

I tried applying this to the eleventy-base-blog starter, which has the 11ty image plugin set up all fancy-like, and run into the old problem of markup showing up in the excerpt, except this time it’s {% image %} Nunjucks markup. And it seems the Render plugin ain’t too fond of "njk,njk,md". Yeesh.

I don’t have a solution for this. If I use 11ty, I guess I will just need to take care to never put images above the cut in a post. Or maybe I will not use 11ty – that’s seeming more likely at this point. If you know a solution to this, I would love to know.

Tags:

Comments are closed.