DEV Community

Nicolas Hoizey
Nicolas Hoizey

Posted on • Originally published at nicolas-hoizey.com on

How I built my own excerpt for Markdown content in Eleventy

I was not really happy with Eleventy's native excerpt solution requiring just a separator and having the excerpt content preserved in the content, without any way to style it differently. So I tried different alternatives, and settled on a solution with some Markdown-it plugins and a bunch of regexes.

To be able to style the content lead whatever it contains, I'm using the great and simple markdown-it-container plugin for Markdown-it, with a lead container.

For exemple, I can write this in the begining of my Markdown file, after the YAML Front Matter:

::: lead
This paragraph is in the lead.

This other paragraph is also in the lead.
:::

This is no more part of the content lead…
Enter fullscreen mode Exit fullscreen mode

With this really simple syntax, I can put whatever I want in the lead and style it in the content page.

And I can also extract it for the excerpt!

I could have used a Nunjucks filter, as I previously did, but it means the excerpt for the same content would have been computed multiple times (unless I did some memoization) for different content listings in the homepage, a category page, archives pages, Atom and JSON feeds, Algolia index, etc.

Fortunately, Eleventy provides the eleventyConfig.setFrontMatterParsingOptions() function which allows passing options to gray-matter, the npm package it relies on to parse front matter.

This is the function that allows setting a custom separator for the default excerpt feature with the excerpt_separator option, as shown in Eleventy documentation, but in addition to the simple true boolean shown here, the excerpt option can be a function, which gets the Markdown content and options as parameters.

Here's the function I built to generate my own excerpt:


const markdownItPlainText = require('markdown-it-plain-text');
const excerptMd = new markdownIt().use(markdownItPlainText);

function grayMatterExcerpt(file, options) {
  const regex = /^.*::: lead(((?!(:::)).|\n)+):::.*$/gm;
  let excerpt = '';
  let leadFound = false;

  if ((leadMatches = regex.exec(file.content)) !== null) {
    lead = leadMatches[1];
    leadFound = true;
    excerptMd.render(lead);
  } else {
    excerptMd.render(file.content);
  }
  excerpt = excerptMd.plainText
    .trim()
    .replace(/{%(((?!(%})).|\n)+)%}/gm, '') // remove short codes
    .replace(/{{(((?!(}})).|\n)+)}}/gm, '') // remove nunjucks variables
    .replace(/{#(((?!(#})).|\n)+)#}/gm, '') // remove nunjucks comments
    .replace(/<style>(((?!(<\/style>)).|\n)+)<\/style>/gm, '') // remove inline CSS
    .replace(
      /<script type="application\/ld\+json">(((?!(<\/script>)).|\n)+)<\/script>/gm,
      ''
    ) // remove JSON+LD
    .replace(/(<\/h[1-6]>)/gm, '. $1') // add a dot at the end of headings
    .replace(/<\/?([a-z][a-z0-9]*)\b[^>]*>|<!--[\s\S]*?-->/gm, '') // remove HTML tags
    .replace(/(\[\^[^\]]+\])/gm, '') // remove Markdown footnotes
    .replace(/\[([^\]]+)\]\(\)/gm, '$1') // remove Markdown links without URL (from {% link_to %} for example)
    .replace(/ +(\.|,)/gm, '$1'); // remove space before punctuation

  if (!leadFound && excerpt.length > 150) {
    // Keep only 145 characters and an ellipsis if there was no declared lead
    excerpt = excerpt.replace(/^(.{145}[^\s]*).*/gm, '$1') + '…';
  }
  file.excerpt = excerpt;
}

eleventyConfig.setFrontMatterParsingOptions({
  excerpt: grayMatterExcerpt,
});

Enter fullscreen mode Exit fullscreen mode

It first looks for a ::: lead container to use directly, or takes the full content.

It then parses the result with Markdown-it and the markdown-it-plain-text plugin, and then removes some parts that are really not useful in an excerpt: Nunjucks short codes/variables/comments, inline CSS rules, JSON/LD metadata, remaining HTML tags, footnotes, etc.

Finally, if the full content was used (no ::: lead found), it limits the result length.

Image of Datadog

The Essential Toolkit for Front-end Developers

Take a user-centric approach to front-end monitoring that evolves alongside increasingly complex frameworks and single-page applications.

Get The Kit

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay