Creating Excerpts in Jekyll with Wordpress-style <!--more--> HTML Comments

March 6, 2011 | Web Development

I just finished converting my blog to Jekyll. If you've never heard of Jekyll, it's a static blog publishing tool. Unlike Wordpress, where the HTML for your blog is generated on-the-fly by your server every time someone accesses your site, Jekyll is meant to run on your PC. You write your posts in a simple text format (often Markdown), run Jekyll on them, and then upload the posts to your server. The nice thing is that your server dosn't need any PHP or Python or Ruby or anything. And, if your website gets a ton of traffic, there's no database to crash. In fact, since you don't need any scripting languages or a database, you can just throw everything on Amazon S3 and be guaranteed that your site will never crash.

The downside of Jekyll is that its features are kind of minimalistic. That minimalism is nice because it means there's less to learn. That minimalism also results in lots of blog posts on the theme of "I switched to Jekyll and it's awesome now that I've hacked it up to implement this Wordpress feature I couldn't live without." On that note, here's how you can put a list of post excerpts on your Jekyll blog's front page and archive pages, with the "fold" marked with a <!--more--> HTML comment just like in Wordpress.

There are lots of pages out there showing how to do similar things by forking Jekyll. But, with the newish Jekyll plugin system, there's no need to mess with the Jekyll core. Instead, just add a plugin that adds a new Liquid filter. Here's postmore.rb, which should go in the _plugins directory in the root of your Jekyll site:

module PostMore
  def postmorefilter(input, url, text)
    if input.include? "<!--more-->"
      input.split("<!--more-->").first + "<p class='more'><a href='#{url}'>#{text}</a></p>"
    else
      input
    end
  end
end

Liquid::Template.register_filter(PostMore)

This creates and registers a filter called postmorefilter. It takes two arguments: the URL of the main post body page, and the text to use on the link to the page. To use it, you just apply it to the post's contents. If the post containes the <!--more--> fold marker, then the text up to the marker will be output, followed by a link to the page with the full post. If there is no marker, then the entire post is output without a link. For example:

{{ page.content | postmorefilter: page.url, "Read the rest of this entry" }}

The next step is to modify your index.html to output your posts. I wanted to put the most recent few posts on my blog's front page, and then have archive pages with older posts. To do that, I had to turn on Jekyll's pagination feature by setting paginate: 6 in my _config.yml (where 6 is the number of posts per page). Then, I had to modify my index.html to use the paginator. Note that if you want to do this in another file, you need to set the paginate_file setting in your _config.yml. Here's what my index.html looks like:

---
layout: twocolumn
title: Jacques Fortier
---

<div id="home">
  <!-- iterate through the posts on this page -->
  {% for page in paginator.posts %}
    <div class="post">
      <h1><a href="{{ page.url }}">{{ page.title }}</a></h1>
      <p class="meta">{{ page.date | date: "%B %e, %Y" }}</p>
        {{ page.content | postmorefilter: page.url, "Read the rest of this entry" }}
    </div>
  {% endfor %}

  <!-- links to prev and next pages for browsing thru archives -->
  {% if paginator.previous_page == 1 %}
    <span id="newer"><a href="/">&laquo;&laquo; Newer Entries</a></span>
  {% elsif paginator.previous_page %}
    <span id="newer"><a href="/page{{ paginator.previous_page }}">&laquo;&laquo; Newer Entries</a></span>
  {% endif %}
  {% if paginator.next_page %}
    <span id="older"><a href="/page{{ paginator.next_page }}">Older Entries &raquo;&raquo;</a></span>
  {% endif %}

</div>

Of course, you would set the title and layout in the YAML front-matter to whatever you're using on your blog.

To summarize: